Django-ORM操作

一、orm概述

1 orm:对象关系映射(跟语言无关)
	数据库中的表     ----》对应程序的一个类
	数据库中的一行数据----》对应程序中的一个对象
2 python中常见orm框架
	-django的orm框架
    -sqlachemy的orm框架
    
3 java:(扩展),java中写web项目
	ssh框架 :spring+struts(有漏洞)+hibernate(orm框架)(很早的年代)
	ssm框架:spring+springmvc+mybatis(orm框架,可以写原生sql)
    springboot:sb框架 ,tomcat内置进去了
    springcloud:微服务
4 orm能干的事
	-创建表(不能创建数据库,手动创建数据库)
    -增加删除表内字段
    -增删查改数据

二、orm简单使用

1.配置文件

虽然使用orm对应与许多的数据库,但是因为我们一般是使用mysql数据库,所以需要在settings中配置,不然默认使用sqlite。若使用sqlite跳过这一步直接从第二步开始。

1.
#settings.py
DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'day62',
            'HOST': '127.0.0.1',
            'PORT': 3306,
            'USER': 'root',
            'PASSWORD':'123'
            # 下面两个暂时不用加,也不用管
            'ATOMIC_REQUEST': True,
        	'OPTIONS': {
            	"init_command": "SET storage_engine=MyISAM",
				}
            }
        }
2.django默认情况链接mysql,用的驱动是mysqldb模块,python 3.x以后,这个模块用不了了,咱们用的全都是pymysql,需要做个替换
# app的__init__.py 中写以下代码,其实随便在哪写,但是规范是在这写
# 因为对于django项目,在运行的时候,所有文件的代码都会运行,所以无所谓在哪写,公司里可能会换地方写
import pymysql
pymysql.install_as_MySQLdb()

2.利用orm创建表

# 第一步在models中写要给类
class Book(models.Model):
    # 如果不写id,会默认一个id,并且自增
    # primary_key=True  表示该字段是主键,一个表中只能有一个主键
    id = models.AutoField(primary_key=True)
    # varchar类型,长度,
    # 字段是否可以为空:null=True,可以为空
    # 默认值:default='未知书名',如果没传,默认是它
    # 设置索引:db_index=True 表示该字段是辅助索引
    # 是否唯一:unique=True 表示唯一
    name=models.CharField(max_length=32,null=True,default='未知书名',db_index=True,unique=True)
    # float类型
    # max_digits 最大长度是5  4567.5
    # decimal_places=2 小数点后两位   23.56     999.99
    price=models.DecimalField(max_digits=5,decimal_places=2)

    # DateTimeField年月日时分秒
    # auto_now=True  新增,默认使用当前时间
    # auto_now_add=True 修改,设置当前时间,这两者只能设置一种
    publish_date=models.DateTimeField(auto_now=True)

'''
    auto_now无论是你添加还是修改对象,时间为你添加或者修改的时间。
    auto_now_add为添加时的时间,更新对象时不会有变动。
'''
    publish=models.CharField(max_length=32)
    
    
# 第二步,把表创建出来(执行两个命令)
    -python3 manage.py makemigrations  # 这条命令会在migrations创建一条记录,数据库变更记录
    -python3 manage.py migrate         # 把更改同步到数据库

3.orm单表增加

from app01 import models

# 注册功能
def register(request):
    if request.method == 'GET':
        return render(request, 'register.html')
    elif request.method == 'POST':
        # 获取数据
        res = request.POST
        name = res.get('name')
        password = res.get('password')
        gender = res.get('gender')
        province = res.get('province')

        # 通过orm存到数据库
        # 方式一
        user=models.UserInfo(name=name,password=password,gender=gender,province=province)
        user.save()

        # 方式二
        user=models.UserInfo.objects.create(name=name,password=password,gender=gender,province=province)

        # 注册完成直接跳转到用户列表页面
        return redirect('/userlist')
      

4.orm单表查询

# 查看目前注册的用户
def user_list(request):
    # 通过orm查询所有用户,返回列表,内部有一个一个的user对象 [user1,user2,user3]
    userlist=models.UserInfo.objects.all()
    return render(request, 'userlist.html',context={'userlist':userlist})

def select_user(request):
    # 查询名字叫xxx的人(是个列表:QuerySet)
    res = models.UserInfo.objects.filter(name='xxx')
    res = models.UserInfo.objects.filter(name='xxx')[0]
    res = models.UserInfo.objects.filter(name='xxx').first()

    # 利用get方法查询名字叫xxx的人(UserInfo),如果没有或者由多个,都报错
    # 查询结果必须有且仅有一个才正常,否则报错
    res=models.Book.objects.get(name='xxx')

三、常用和非常用字段和参数概览

1.常用字段

# 最为常用
IntegerField   整数
AutoField
BooleanField
CharField
DateField
DateTimeField
DecimalField
FileField   上传文件,本质是varchar
ImageField   图片,本质是varchar,继承了FileField
TextField   存大文本
EmailField   本质是varchar

# 详细
    AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    自定义无符号整数字段

        class UnsignedIntegerField(models.IntegerField):
            def db_type(self, connection):
                return 'integer UNSIGNED'

        PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
            'AutoField': 'integer AUTO_INCREMENT',
            'BigAutoField': 'bigint AUTO_INCREMENT',
            'BinaryField': 'longblob',
            'BooleanField': 'bool',
            'CharField': 'varchar(%(max_length)s)',
            'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
            'DateField': 'date',
            'DateTimeField': 'datetime',
            'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
            'DurationField': 'bigint',
            'FileField': 'varchar(%(max_length)s)',
            'FilePathField': 'varchar(%(max_length)s)',
            'FloatField': 'double precision',
            'IntegerField': 'integer',
            'BigIntegerField': 'bigint',
            'IPAddressField': 'char(15)',
            'GenericIPAddressField': 'char(39)',
            'NullBooleanField': 'bool',
            'OneToOneField': 'integer',
            'PositiveIntegerField': 'integer UNSIGNED',
            'PositiveSmallIntegerField': 'smallint UNSIGNED',
            'SlugField': 'varchar(%(max_length)s)',
            'SmallIntegerField': 'smallint',
            'TextField': 'longtext',
            'TimeField': 'time',
            'UUIDField': 'char(32)',

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值

    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

    BinaryField(Field)
        - 二进制类型

2.常用参数

(1)null
 
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
 
(2)blank
 
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
 
(3)default
 
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。
 
(4)primary_key
 
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
 
(5)unique
 
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
 
(6)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。如果设置了choices,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices中的选项。

3.元信息

class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)

    class Meta:

    # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
    db_table = "table_name"

    # 联合索引
    index_together = [
        ("pub_date", "deadline"),
    ]

    # 联合唯一索引
    unique_together = (("driver", "restaurant"),)
    # admin中显示的表名称
    verbose_name = '用户信息表'
    # 只写上面一行的话verbose_name会加s,即用户信息表s,因为在外国s代表复数,如果要完全改过来写下面这句
    verbose_name_plural = '用户信息表'
    # 一般这两句是连用的,但是真正起效果的是下面那句

四、打印原生sql

有时候为了我们方便排查需要打印出原生sql查看

配置文件粘贴
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

五、查询表记录API

<1> all():                  查询所有结果
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象,即排除
<5> order_by(*field):       对查询结果排序,如果要对某个字段逆排序,可以在字段前加-号('-id')
<6> reverse():              对查询结果反向排序,相当于上面加-号
<7> count():                返回数据库中匹配查询(QuerySet)的对象数量。count操作是在数据库的时候就统计好的,而不是拿到结果再用python统计
<8> first():                返回第一条记录,底层相当于asc,limit1
<9> last():                返回最后一条记录,底层相当于desc,limit1
<10> exists():              如果QuerySet包含数据,就返回True,否则返回False
<11> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<12> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct():            从返回结果中剔除重复纪录

六、基于双下划线的模糊查询

# 1 价格在[100,200,300]这个范围内
Book.objects.filter(price__in=[100,200,300])
# 2 大于,小于,大于等于,小于等于
Book.objects.filter(price__gt=100)
Book.objects.filter(price__lt=100)
Book.objects.filter(price__gte=100)
Book.objects.filter(price__lte=100)
# 3 范围
Book.objects.filter(price__range=[100,200])
# 4 包含
Book.objects.filter(title__contains="python")
# 5 忽略大小写包含
Book.objects.filter(title__icontains="python")
# 6 以xx开头
Book.objects.filter(title__startswith="py")
# 7 时间类型,年份是2012年的
Book.objects.filter(pub_date__year=2012)

七、删除表记录

# 第一种:queryset的delete方法
res=models.Book.objects.all().delete()
print(res)
# 第二种:对象自己的delete方法
book = models.Book.objects.all().filter(name='金梅').first()
print(type(book))
res=book.delete()

八、修改表记录

# 第一种:queryset的update方法
res=models.Book.objects.filter(publish='东京').update(name='金梅1')
print(res)
# 第二种:对象自己的
book = models.Book.objects.filter(name='xxx').last()
book.name='asdfasd'
book.save()

九、python脚本中调用django环境

可以让我们不需要打开浏览器输入数据,方便测试

# 在脚本中调用djagno服务
import os
if __name__ == '__main__':
    #1  引入django配置文件
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day67.settings')
    # 2 让djagno启动
    import django
    django.setup()
    # 3 使用表模型
    from app01 import models
    models.Book.objects.create(name='测试书籍',publish='xx出版社')

十、多表操作

1.多表模型创建注意点

多表因为要创建外键或者对应关系,所以和单表的创建有些许不同

1 图书表:book,作者表:author,作者详情表:authordetail,出版社表:publish,(第三张中间表)
2 作者跟作者详情:是一对一,关联字段写在哪一方都可以
3 图书跟出版社:是一对多,一对多关系一旦确立,关联字段写在多的一方
4 图书和作者:是多对多,多对多的关系需要建立第三张表(可以自动生成)

5 models.py中把关系建立出来
from django.db import models
### django版本:目前使用1.11.x或者12.0.x,使用django3会出问题

class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    phone = models.CharField(max_length=64)
    email = models.EmailField()


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    publish_date = models.DateTimeField(auto_now_add=True)
    read_num=models.IntegerField(default=0)
    commit_num=models.IntegerField(default=0)

    # to='Publish'跟Publish表做关联(ForeignKey,一对多)
    # to_field='id'跟哪个字段做关联
    # publish=models.CharField(max_length=32)
    # publish=models.ForeignKey(to='Publish',to_field='id')
    # publish = models.ForeignKey(to='Publish')  # 不写的话,默认跟主键做关联
    publish = models.ForeignKey(to=Publish)

    # 自动创建出第三张表(这句话会自动创建第三张表)
    # authors在数据库中不存在该字段,没有to_field
    # 默认情况:第三张表有id字段,还有Book表的id和Author表的id字段
    authors=models.ManyToManyField(to='Author')


class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.SmallIntegerField()
    # 一对一的本质是  ForeignKey+unique
    author_detail=models.OneToOneField(to='AuthorDetail',to_field='id')
    # author_detail=models.ForeignKey(to='AuthorDetail',to_field='id',unique=True)


class AuthorDetail(models.Model):
    id = models.AutoField(primary_key=True)
    sex = models.SmallIntegerField()
    addr = models.CharField(max_length=64)
    phone = models.BigIntegerField()
    
 
6 同步到mysql数据库
	-配置文件
    -pymysql.install_as_mysqldb()
		-g的mysqlclient
    -两条命令
    
7 2.x版本的django
	-外键字段必须加参数:on_delete
    -1.x版本不需要,默认就是级联删除
    -假设,
    	删除出版社,该出版社出版的所有图书也都删除,on_delete=models.CASCADE
        删除出版社,该出版社出版的图书不删除,设置为空on_delete=models.SET_NULL,null=True
        删除出版社,该出版社出版的图书不删除,设置为默认on_delete=models.SET_DEFAULT,default=0

2.一对多添加记录

当创建了外键关系之后,我们在插入表格的时候,被关联的表格需要先有数据,然后才能在多关系的表格里加数据,如下的book表,publish字段需要和publish表的id字段关联。

publish=models.Publish.objects.create(name='北京出版社',addr='北京',phone='0536-12345678',email='邮箱地址')
# 新增图书三种方式
book=models.Book.objects.create(name='金梅',price='23.45',publish=publish)# publish=对象
book=models.Book.objects.create(name='西游记',price='23.55',publish_id=1)# publish_id=数字
book=models.Book.objects.create(name='西游记',price='23.55',publish_id=publish.id)# publish_id=数字


# 总结:
1 email可以不传email,本质就是varchar(admin中会判断)
2 新增图书:
    -publish=publish
    -publish_id=publish.id
3 写在表模型中的publish字段,到数据库中会变成publish_id(ForeignKey)
4 查到book对象以后
    -book.publish     对象
    -book.publish_id  id号,数字

3.多对多添加记录,修改,删除

自动创建的表,表模型就拿不到,就如同我们在book类里创建的authors。book.authors代指表模型

# 多对多,作者和书
# 给西游记这本书新增两个作者lqz和egon
# 去到西游记这本书
book=models.Book.objects.get(name='西游记')
# 代指中间表book.authors
lqz=models.Author.objects.get(id=2)
egon=models.Author.objects.get(id=3)
# 新增作者需要传id,有以下三种方式
book.authors.add(2,3) # 新增作者,通过id新增
book.authors.add(lqz,egon) # 新增作者,通过对象新增
book.authors.add(2,egon) # 新增作者,通过对象新增

# 西游记删除一个作者
book = models.Book.objects.get(name='西游记')
book.authors.remove(2)
egon = models.Author.objects.get(id=3)
book.authors.remove(egon)

# clear 清空所有作者
book = models.Book.objects.get(name='西游记')
book.authors.clear()

# set先清空,再add,前提是不存在的作者,即如果这个作者原来在表里,又使用set,原存在的数据不会被删除,然后把不存在的数据加进去
book.authors.set([4,])

# add ,remove,set clear

十一、多表操作之查询

跨表查询有两种方式

-基于对象的跨表查询:相当于我们原生sql子查询
-基于双下划线的跨表查询:相当于我们原生sql的关联查询,连表查询(就是left join等)

1.基于对象的跨表查询

基于对象的跨表查询,先查对象,通过对象再去查另一个对象(正向:字段名,反向:表名小写/表名小写_set.all())

正向查询:最后查出来的对象有我们需要的属性,那么直接,对象.字段名
反向查询:最后查出来的对象没有我们需要的属性,而是与我们想要的字段有关联(比如外键关系),那么我们可以,对象.类名小写.属性,获取单个数据,对象.类名小写_set.all(),获取多个数据返回的列表。

技巧:熟练后,不需要管什么正向反向,能点出属性就直接点出来,不能的话就点类名小写,再看数据是一条还是多条。

一对多

# 查询主键为1的书籍的出版社所在的城市
# 先查主键为1的书,下面有两种方式
book=models.Book.objects.get(id=1) # 第一次查询
book=models.Book.objects.filter(id=1).first() # 第一次查询
publish=book.publish  # 第二次查询,内部又执行了一次查询,根据publish_id查询publish
print(publish.addr)

# 北京出版社出版的所有书籍,反向
publish=models.Publish.objects.get(name='北京出版社')  # 第一次查询了出版社
books=publish.book_set.all()    # 表名小写_set,第二次,根据出版社id,查询所有书
print(books)

# 地址为山东的作者写的所有书
author_detail=models.AuthorDetail.objects.get(addr='山东')
author=author_detail.author
books=author.book_set.all()
print(books[0].name)

# 正向查询:book表内有publish字段 直接对象.字段名
# 反向查询:publish表内没有book字段,出版社对象.Book小写_set.all()或者Book直接小写。

一对一

# 查询所有住址在山东的作者的姓名,反向
# 反向查询:author_detail没有author字段,需要使用author_detail.表名小写
author_detail=models.AuthorDetail.objects.filter(addr__contains='山东').first()
print(author_detail.author.name)

# 查询egon作者的地址,正向
author=models.Author.objects.get(name='egon')
print(author.author_detail.addr)

多对多

# 金梅所有作者的名字以及手机号,正向
book=models.Book.objects.get(name='金梅')
authors=book.authors.all()
for author in authors:
	print(author.name)
	print(author.author_detail.phone)

# 查询egon出过的所有书籍的名字,反向
egon=models.Author.objects.get(name='egon')
books=egon.book_set.all()
for book in books:
	print(book.name)

2.基于双下划线的跨表查询

一对多

# 格式 models.类型.objects.filter,values/values_list(写 __ 跨表)

# 查询北京出版社出版过的所有书籍的名字与价格(一对多)
res=models.Publish.objects.filter(name='北京出版社').values('book__name','book__price')
print(res)
res=models.Book.objects.filter(publish__name='北京出版社').values('name','price')
print(res)

多对多

# 查询egon出过的所有书籍的名字,价格(多对多)
# 反向
res=models.Author.objects.filter(name='egon').values('book__name','book__price')
print(res)

# 正向
res=models.Book.objects.filter(authors__name='egon').values('name','price')
print(res)

# 查询egon的手机号
res=models.Author.objects.filter(name='egon').values('author_detail__phone')
print(res)
res=models.AuthorDetail.objects.filter(author__name='egon').values('phone')
print(res)

3.进阶连续跨表查询

# 查询北京出版社出版过的所有书籍的名字以及作者的姓名
res=models.Publish.objects.filter(name='北京出版社').values('book__name','book__authors__name')
print(res)

res=models.Book.objects.filter(publish__name='北京出版社').values('name','authors__name')
print(res)

res=models.Author.objects.filter(book__publish__name='北京出版社').values('book__name','name')
print(res)

# 手机号以189开头的作者出版过的所有书籍名称以及出版社名称
res=models.AuthorDetail.objects.filter(phone__startswith='189').values('author__book__name','author__book__publish__name')
print(res)

res = models.Author.objects.filter(author_detail__phone__startswith='189').values('book__name','book__publish__name')
print(res)

十二、聚合查询

聚合查询包括:Count,Avg,Min,Max,Sum,和我们原生sql中的聚合函数是一样的。同时使用也相同,我们在使用了聚合函数之后,只能拿到聚合的字段。

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

from django.db.models import Avg,Max,Min,Count,Sum
# 要用aggregate药包裹聚合函数,当aggregate结束,已经不是queryset对象了,我们也可以在aggrepate里用as起别名
# 1 计算所有图书的平均价格
book=models.Book.objects.all().aggregate(Avg('price'))
book=models.Book.objects.all().aggregate(avg=Avg('price'))
# 2 计算总图书数
book = models.Book.objects.all().aggregate(count=Count('id'))
# 3 计算最低价格的图书
book = models.Book.objects.all().aggregate(min_price=Min('price'))
# 4 计算最大价格图书
book = models.Book.objects.all().aggregate(max_price=Max('price'))
# 5 多个查询
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))

十三、分组查询

分组查询,即原生sql里的group by。我们经常是分组和聚合函数搭配使用,如果是单独使用聚合函数,那么就是用aggregate()包括聚合函数,如果要和分组搭配使用,就需要annotate()包裹。

annotate的返回值是querySet,如果不想遍历对象,可以用上value_list:

queryResult= Publish.objects.annotate(MinPrice=Min("book__price")).values_list("name","MinPrice")
print(queryResult)
'''
使用说明:
annotate() 内写聚合函数
values在前表示group by的字段
values在后表示取某几个字段
filter在前表示where
filter在后表示having

pk 代指主键
如果没有指定group by的字段,默认就用基表(Publish)主键字段作为group by的字段

查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
如果不用分组的表作为基表,数据不完整可能会出现问题
'''
from django.db.models import Avg, Count, Max, Min
# 示例一:查询每一个出版社id,以及出书平均价格
select publish_id,avg(price) from app01_book group by publish_id;
ret=models.Book.objects.values('publish_id').annotate(avg=Avg('price')).values('publish_id','avg')
print(ret)

# 查询出版社id大于1的出版社id,以及出书平均价格
select publish_id,avg(price) from app01_book where publish_id>1 group by publish_id;
ret=models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg=Avg('price')).values('publish_id','avg')
print(ret)

# 查询出版社id大于1的出版社id,以及出书平均价格大于30的
select publish_id,avg(price)as aaa from app01_book where publish_id>1 group by publish_id HAVING aaa>30;
ret = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg=Avg('price')).filter(avg__gt=30).values('publish_id', 'avg')
print(ret)

# 查询每一个出版社出版的书籍个数
# pk 代指主键
ret=models.Publish.objects.values('pk').annotate(count=Count('book__id')).values('name','count')
print(ret)
# 如果没有指定group by的字段,默认就用基表(Publish)主键字段作为group by的字段.即上面可缩写为
ret=models.Publish.objects.annotate(count=Count('book__id')).values('name','count')
print(ret)

# 另一种方式实现
ret=models.Book.objects.values('publish').annotate(count=Count('id')).values('publish__name','count')
print(ret)

# 查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
# 如果不用分组的表作为基表,数据不完整可能会出现问题
ret=models.Author.objects.values('pk').annotate(max=Max('book__price')).values('name','max')
ret = models.Author.objects.annotate(max=Max('book__price')).values('name', 'max')
ret= models.Book.objects.values('authors__id').annotate(max=Max('price')).values('authors__name','max')
print(ret)

# 查询每一个书籍的名称,以及对应的作者个数
ret=models.Book.objects.values('pk').annotate(count=Count('authors__id')).values('name','count')
ret=models.Book.objects.annotate(count=Count('authors__id')).values('name','count')
ret=models.Author.objects.values('book__id').annotate(count=Count('id')).values('book__name','count')
print(ret)

#统计不止一个作者的图书
ret=models.Book.objects.values('pk').annotate(count=Count('authors__id')).filter(count__gt=1).values('name','count')
ret = models.Author.objects.values('book__id').annotate(count=Count('id')).filter(count__gt=1).values('book__name', 'count')
print(ret)

# 统计价格数大于10元,作者的图书
ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).values('name', 'count')
print(ret)

#统计价格数大于10元,作者个数大于1的图书
ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).filter(count__gt=1).values('name', 'count')
print(ret)
ret = models.Book.objects.filter(price__gt=10).annotate(count=Count('authors__id')).filter(count__gt=1).values('name', 'count')
print(ret)

十四、F和Q查询

F查询:取出数据库的某个字段的值,因为

Q查询:制造"与或非"的条件

# F查询:取出数据库的某个字段的值
from django.db.models import F
# 把read_num都加1
ret=models.Book.objects.all().update(read_num=F('read_num')+1)
print(ret)

#查询评论数大于阅读数的书籍
ret=models.Book.objects.all().filter(commit_num__gt=F('read_num'))
for i in ret:
    print(i.name)

# 查询评论数大于阅读数2倍的书籍
ret=models.Book.objects.filter(commit_num__gt=F('read_num')*2)
print(ret)
    
    
# Q查询:制造"与或非"的条件:& | ~
from django.db.models import Q
# 查询名字叫egon或者价格大于100的书
ret=models.Book.objects.filter(Q(name='egon') | Q(price__gt=100))
# 查询名字叫egon并且价格大于100的书,并且的用法有两种方式,一般用第二种,和之前的写法一样
ret=models.Book.objects.filter(Q(name='egon') & Q(price__gt=100))
ret=models.Book.objects.filter(name='egon',price__gt=100)
# 查询名字不为egon的书
ret = models.Book.objects.filter(~Q(name='egon'))
print(ret)

# Q可以嵌套,嵌套就可以使用很多条件的判断
ret = models.Book.objects.filter((Q(name='egon') & Q(price__lt=100)) | Q(id__lt=3))
print(ret)

十五、原生sql

# 原生sql(有些sql用orm写不出来,因为水平不到位哈哈)
# 第一种:用的比较少
from django.db import connection

cursor = connection.cursor()

cursor.execute("""SELECT * from app01_book where id = %s""", [1])

row = cursor.fetchone()
row = cursor.fetchall()
print(row)

# 第二种,用的多
books=models.Book.objects.raw('select * from app01_book where id >3')
print(books)#RawQuerySet对象
for book in books:
    print(book.name)

books=models.Book.objects.raw('select * from app01_publish')
for book in books:
    print(book.__dict__)
    print(book.name)
    print(book.addr)
    print(book.email)
    print(book.price)

authors = models.Author.objects.raw('SELECT app01_author.id,app01_author. NAME,app01_authordetail.sex FROM app01_author JOIN app01_authordetail ON app01_author.author_detail_id = app01_authordetail.id WHERE app01_authordetail.sex = 1')

for author in authors:
    print(author.name)
    print(author.__dict__)

十六、defer和only

# defer和only(查询优化相关)
# only保持是book对象,但是只能使用only指定的字段
books = models.Book.objects.all().only('name')
print(books[0].name)
print(books[0].price)  # 能出来,但是要再去数据库查一遍

books = models.Book.objects.all().only('name')
print(books[0].__dict__)
books = models.Book.objects.all().defer('name','price')
print(books[0].__dict__)

十七、事务(请求,装饰器,局部)

# 事物:ACID,事物的隔离级别,锁, 行级锁,表级锁
# djanog orm中使用事物:原子性操作,要么都成功,要么都失败
# 例子:新增一个作者详情,新增一个作者,这两者应该同时成功,不可能有作者详情而没有作者,反之亦然

# 事物的三个粒度
# 1 局部使用(常用)
from django.db import transaction

with transaction.atomic():  # 都在事物中,要么都成功,要么都失败
    author_detail = models.AuthorDetail.objects.create(addr='xxx', phone='123', sex=1)
    # raise Exception('抛了异常')
    author = models.Author.objects.create(name='llqz', age=19, author_detail=author_detail)
    
# 2 在一个视图中使用,视图函数装饰器,这一个视图函数都在一个事物中(偶尔使用)
@transaction.atomic
def index(request):
    return HttpResponse('ok')

# 3 整个http请求,在事物中,在setting.py中配置(基本不用)
DATABASES = {
    'default': {
        ...
        'PORT': 3306,
        'ATOMIC_REQUEST': True,

    }
}
'ATOMIC_REQUEST': True,
# 设置为True统一个http请求对应的所有sql都放在一个事务中执行,百分之九十九不会用这个,因为把整个http请求放进事务太消耗资源,且很多情况下,对于一个http请求我们需要有不同的操作

补充

1.时区和国际化问题

setting.py中
1 后台管理汉语问题
	LANGUAGE_CODE = 'zh-hans'  # 管理后台看到的就是中文
2 时区问题(使用东八区)
	TIME_ZONE = 'Asia/Shanghai'
    USE_TZ = False

2.django admin

0 管理后台是django提供的可以快速对表进行增删查改操作

1 创建一个后台管理账号
    python3 manage.py createsuperuser
    输入用户名
    输入邮箱(可以不填,敲回车)
    输入密码
    确认密码
    # 超级用户创建出来了,可以登录管理后台了
2 admin中表中一行一行的数据显示我们定制的样子
	重写模型类的__str__方法
3 创建完账号后,需要将表注册到后台中
    在app下的admin.py中写
    from app01 import models
    # 把book表注册,管理后台就能看到了
    admin.site.register(models.Book)

4 更改时区,这样django后台就变成了中文

3.blank参数作用

1 需要把book表注册到admin中
	在app下的admin.py中写
    from app01 import models
	# 把book表注册一些,管理后台就能看到了
	admin.site.register(models.Book)
2 可以快速的对book表进行增删查改操作

4.看一看这篇博客

https://www.cnblogs.com/nokiaguy/p/13803370.html

5.安装模块相关

pip3 install django    
# 本质是去https://pypi.python.org/simple,搜这个模块,会根据你的平台下载在一个安装包(windows平台是whl),下载完,再安装

# pip安装失败的情况
# 我们可以绕过它,有了whl文件以后,自己装
# https://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv
pip3 install django.whl   

# 官方库没有上传到pypi,官方也不给制作whl文件
#如何安装  包 (setup.py)
到达安装目录,setup.py所在的目录
python setup.py build
python setup.py install

# 配置清华源,豆瓣源,本质是
豆瓣源会把pypi,包拉到自己的服务器上,以后你再下,去它的服务器上下,所以速度快

# 你自己写的包,如何上传到pypi上给别人使用?

6.外键关系要不要建立

1 关联字段与外键约束没有必然的联系(建管理字段是为了进行查询,建约束是为了不出现脏数据)
2 默认情况,关联关系建好以后,外键约束就自然建立了
3 实际工作中,外键约束一般不建(影响效率),都是人为约束(代码约束)
	-db_constraint=False
4 表模型和数据库表的对应,不要直接修改表(这是可以的,但是不建议),要修改表模型,同步到表中

练习

1.链式调用

queryset就是链式调用的使用),实现一个可以支持链式调用的类

class MyClass:

    def func1(self):
        print('func1')
        return self

    def func2(self):
        print('func2')
        return self


obj = MyClass()
obj.func1().func2()

(重点)2.利用orm的多表查询完成如下功能

基于对象,基于双下滑下)

# 查找所有书名里包含红楼的书
    res = models.BookInfo.objects.filter(name__contains='红楼')
    # 查找出版日期是2017年的书
    res = models.BookInfo.objects.filter(publish_time__year=2017)
    # 查找出版日期是2017年的书名
    books = models.BookInfo.objects.filter(publish_time__year=2017)
    for book in books:
        print(book.name)
    # 查找价格大于10元的书
    res = models.BookInfo.objects.filter(price__gt=10)
    # 查找价格大于10元的书名和价格
    res = models.BookInfo.objects.filter(price__gt=10)
    for i in res:
        print(i.name,i.price)
    # 查找在北京的出版社
    res = models.Publish.objects.filter(addr='北京')
    # 查找名字以沙河开头的出版社
    res = models.Publish.objects.filter(name__startswith='沙河')
    # 查找作者名字里面带“小”字的作者
    res = models.Author.objects.filter(name__contains='小')
    # 查找年龄大于30岁的作者
    res = models.Author.objects.filter(age__gt=30)
    # 查找手机号是155开头的作者
    res = models.AuthorDetail.objects.filter(phone__startswith='155')
    res1 = res.author_set.all()
    # 查找手机号是155开头的作者的姓名和年龄
    res = models.AuthorDetail.objects.filter(phone__startswith='155')
    res1 = res.author_set.all()
    for i in res1:
        print(i.name,i.age)
    # 查找书名是“红楼梦”的书的出版社
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.publish
    # 查找书名是“红楼梦”的书的出版社所在的城市
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.publish.addr
    res2 = res1.addr
    # 查找书名是“红楼梦”的书的出版社的名称
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.publish
    res2 = res1.name
    # 查找书名是“红楼梦”的书的所有作者
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.author_set.all()
    # 查找书名是“红楼梦”的书的作者的年龄
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.author_set.all()
    for i in res1:
        print(i.age)
    # 查找书名是“红楼梦”的书的作者的手机号码
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.author_set.all()
    res2 = res1.authordetail.phone
    # 查找书名是“红楼梦”的书的作者的地址
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.author_set.all()
    res2 = res1.authordetail.addr
    # 查找书名是“红楼梦”的书的作者的邮箱
    res = models.BookInfo.objects.filter(name='红楼梦')
    res1 = res.author_set.all()
    res2 = res1.authordetail.email

3.完成如下功能

1 查询老男孩出版社出版过的价格大于200的书籍
res = models.BookInfo.objects.filter(price__gt = 200).first()
2 查询2017年8月出版的所有以py开头的书籍名称
res = models.BookInfo.objects.filter(publish_time__year = 2017).filter(name__startswith = 'py')
3 查询价格为50,100或者150的所有书籍名称及其出版社名称
4 查询价格在100到200之间的所有书籍名称及其价格
5 查询所有人民出版社出版的书籍的价格(从高到低排序,去重)

4.整理

查询api--14个方法中,哪些返回值是queryset对象,哪些不是queryset对象,是什么?

5.登录功能,连接mysql

前端页面略

路由

    url(r'^login', views2.login),
    url(r'^index', views2.index),
    url(r'^test', views.test),

视图函数

def index(request):
    return render(request, 'index2.html')


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        name = request.POST.get('name')
        password = request.POST.get('password')
        # 创建一个数据库链接
        conn = pymysql.connect(host='127.0.0.1', user='root', password='123', database='userinfo', port=3306, )
        # 拿到一个游标
        cursor = conn.cursor()
        # 执行sql
        cursor.execute('select * from user where name=%s and password=%s ', (name, password))
        # 获取结果
        ret = cursor.fetchone()
        print(ret)
        if ret:
            return redirect('/index')
        else:
            return HttpResponse('用户名或密码错误')

6.(重点)完成如下查询

import os

if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','django6.settings')
    import django
    django.setup()
    from app01 import models
    from django.db.models import Count, Max, Min,Avg,Sum,F,Q
    # 1统计每一本书作者个数
    ret = models.Book.objects.values("id").annotate(count = Count('authors__id')).values("name","count")
    print(ret)
    # 2统计每一个出版社的最便宜的书
    ret = models.Publish.objects.annotate(min_price = Min('book__name')).values("name",'min_price')
    print(ret)
    # 3统计每一本以py开头的书籍的作者个数:
    ret = models.Book.objects.filter(name__startswith = 'book').annotate(count=Count('authors__id')).values("name", 'count')
    print(ret)
    # 4作者数量大于2的图书名字和价格
    ret = models.Book.objects.annotate(count = Count('authors__id')).filter(count__gt = 2).values('name','price')
    print(ret)
    # 5根据一本图书作者数量的多少对查询集QuerySet进行排序:
    ret = models.Book.objects.annotate(count=Count('authors__id')).values('name','count').order_by('count')
    print(ret)
    # 6查询各个作者出的书的总价格
    ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')
    print(ret)
    # 7查询每个出版社的名称和书籍个数
    ret = models.Publish.objects.annotate(count=Count('book__id')).values('name', 'count')
    print(ret)
    # 8查询所有书籍的平均价格
    ret = models.Book.objects.aggregate(avg_price=Avg('price'))
    print(ret)
    # 9统计图书平均价格和最大价格
    ret = models.Book.objects.aggregate(avg_price=Avg('price'),max_price = Max('price'))
    print(ret)
    # 10统计图书总个数和平均价格
    ret = models.Book.objects.aggregate(count_price=Count('pk'),avg_price=Avg('price')).
    print(ret)
    # 11查询评论数小于等于阅读数2倍的书籍
    ret = models.Book.objects.filter(commit_num__lte=F('read_num')*2)
    for book in ret:
        print(book.name)
    # 12将每一本书的价格提高30元
    ret = models.Book.objects.all().update(price = F("price")+30)
    print(ret)
    # 13将id大于3的每本书价格提高5元
    ret = models.Book.objects.filter(id__gt=3).update(price = F("price")+5)
    print(ret)
    # 14查询名字不是西游,并且价格大于150的书
    ret = models.Book.objects.filter(~Q(name='西游') & Q(price__gt = 150))
    for book in ret:
        print(book.name)
    # 15查询名字叫西游并且价格大于150或者id小于等于5的图书
    ret = models.Book.objects.filter(Q(name='西游') & Q(price__gt = 150) | Q(id__lte=5))
    for book in ret:
        print(book.name)
    # 16查询名字叫西游或者出版社为北京出版社的图书
    ret = models.Book.objects.filter(Q(name='西游') | Q(publish__name = '北京出版社'))
    for book in ret:
        print(book.name)
posted @ 2020-12-01 10:23  王寄鱼  阅读(193)  评论(0编辑  收藏  举报