Django之ORM

Django连接MySQL数据库

1、在settings配置文件将sqlite3数据库配置改成mysql数据裤配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Django_db',
        'USER':'root',
        'PASSWORD':'root',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'CHARSET':'utf8'
    }
}

2、在项目名下或者任意应用下的__init__.py文件中声明用pymysql模块,不用默认的mysqldb模块,代码如下:

import pymysql
pymysql.install_as_MySQLdb()

Django的ORM模型

ORM: 是指对象关系的映射,主要是通过python对象代码简单快捷操作数据库,该模型的不足之处:封装程度太高,有时候效率偏低,需要自己写sql语句

ORM模型常用的字段:

AutoField

int自增列,必填参数primary_key=True。即如果model没有自增列,它会自动创建一个列名为id的列

IntegerField

整数类型,范围在-2147483648 to 2147483647 

DecimalField

浮点数,参数max_digits=8表示八位,decimal_places=2表示小数点占两位

CharField

字符串类型,必须提供max_length参数,用来表示字符长度

注意:该字段对应的是MySQL数据库中varchar类型,如果要使用mysql数据库中字符串的其他类型,则需要自己定义

from django.db import models

# Create your models here.
#Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
    '''
    自定义的char类型的字段类
    '''
    def __init__(self,max_length,*args,**kwargs):
        self.max_length=max_length
        super().__init__(max_length=max_length,*args,**kwargs)

    def db_type(self, connection):
        '''
        限定生成的数据库表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        '''
        return 'char(%s)'%self.max_length
#应用上面自定义的char类型
class Class(models.Model):
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=32)
    class_name=FixCharField(max_length=16)
    gender_choice=((1,''),(2,''),(3,'保密'))
    gender=models.SmallIntegerField(choices=gender_choice,default=3)

自定义及使用
自定义字段类型

DataField

日期字段,日期格式2019-10-11,跟pythondatetime.date()实例一样

DateTimeField

日期字段,格式:YYYY-MM-DD HH:MM 跟python中datetime.datetime()实例

'''
 DateTimeFiled有两个参数:
     auto_now:每次操作数据的时候,该字段会自动将当前时间更新
     auto_now_add:在创建数据的时候会自动将当前创建时间记录下列,之后只要不认为的修改,就不会改变
'''

EmailField,是varchar(254)

BooleanField(Field)  布尔值类型

  该字段传布尔值(False/True)  数据库里存0/1

TextFiled(Field)      文本类型,没有字数限制

FileField(field)      字符类型

  upload to="/data"   该字段存的是文件路径

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

    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)
        - 二进制类型

 字段合集
字段合集

 

常用字段的参数

null:用于表示某个字段为空

unique:设置为unique=True,则表示该字段在表中是唯一的,例如:ForeignKey(unique=True) 表示创建一对一

db_index:db_index=True,代表该字段设置为索引

default:该字段设置为默认值

db_constraint=False: 表示逻辑上关联,但是实际上不受影响

#db_constraint=False,此时,如果删除出版社,对应图书不会被删除,而是出版社字段设置为null
publish=models.ForeignKey(to='Publish',db_constraint=False,on_delete=models.DO_NOTHING)

 

参数choices的使用:

class User(models.Model):
    name=models.CharField(max_length=32)
    sex_choices={
        (1,''),
        (2,''),
        (3,'其他'),
    }
    sex=models.IntegerField(choices=sex_choices)

res=models.User.objects.all()
for i in res:
     print(i.get_sex_display())
'''
总结:如果元组第一个不是数字,是字符串,则用字符串字段。
        不在元组内的数字也可以存,不会报错
        获取choices参数字段的对应的值:固定语法 get_字段名_display
'''    

 

ORM模型的数据库操作步骤:

1、建表,在应用下的models文件相应的数据库操作代码,具体例子如下:

from django.db import models

# Create your models here.
class User(models.Model):
    #id int primarykey auto_icrement
    id=models.AutoField(primary_key=True,verbose_name='主键')
    #username varchar(32)
    username=models.CharField(max_length=32,verbose_name='账号')
    #password int
    password=models.IntegerField(verbose_name='密码')
    '''
    注意:CharField必须指定max_length参数,不然会直接报错
    verbose_name这个参数是用来对字段的解释,可加可不加
    '''

2、在命令窗口执行数据库迁移的两条命令

python manage.py makemigrations

python manage.py migrate

注意:只要每次修改了models文件中和数据库操作有关的代码就必须重新执行上面两条命令。

3、字段的增删改

(1)字段的增加:直接在建表类下添加一个字段

#字段增加的第一种方法,根据命令窗口提示,在命令行窗口输入了新增字段的默认值
age=models.IntegerField(verbose_name='年龄')
#第二种方法,直接添加一个null=True的参数,让新增字段为空
sex=models.CharField(max_length=16,verbose_name='性别',null=True)
#第三种方法,直接添加一个默认值
hobby=models.CharField(max_length=32,verbose_name='爱好',default='study')

(2)字段的修改:直接将原有的字段那一行代码注释,重新写一行

# password=models.IntegerField(verbose_name='密码')
password=models.CharField(max_length=32,verbose_name='密码')

(3)字段的删除:直接把相应字段的那一行代码注释掉就可以。

注意:以上操作,每次都需要数据库迁移的两条命令。同时,字段的注释要仔细,因为字段的删除相应的数据也会跟着删除

数据的增删改查

1、数据的查询:

def login(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get("password")
        #res返回的是一个列表,列表里是存放数据对象,[数据对象1,数据对象2]
        #first()方法,是获取列表中的第一个对象
        res = models.User.objects.filter(username=username).first()
        if res:   #res有对象,则表明该用户存在
            if password==res.password:
                return HttpResponse('登陆成功'.encode())
            else:
                return HttpResponse('密码错误'.encode())
        else:
            return HttpResponse('用户不存在'.encode())
    return render(request,'login.html')

2、数据的插入;

第一种:用User中的create方法创建一个对象

reg=models.User.objects.create(username=username,password=password)

第二种:先实例化一个User对象,并保存

reg=models.User(username=username,password=password)

reg.save()

        elif aciton=="注册":
            if res:
                return HttpResponse('该用户名已存在'.encode())
            else:
                #插入数据的第一种方法
                # reg=models.User.objects.create(username=username,password=password)
                #第二种方法:直接先实例化一个对象,并保存
                reg=models.User(username=username,password=password)
                reg.save()
                return HttpResponse('注册成功'.encode())

 3、修改数据

def edit(request):
    id=request.GET.get('edit_id')
    edit_boj=models.User.objects.filter(id=id).first()
    if request.method=="POST":
        username = request.POST.get('username')
        password=request.POST.get('password')
        '''修改数据方法1
        models.User.objects.filter(id=id).update(username=username,password=password)
        方法1只修改,要修改的字段,其他字段不会修改。
        '''
        #修改数据的方法2
        edit_boj.username=username
        edit_boj.password=password
        edit_boj.save()
        '''
        方法2的效率比较低,因为无论字段是否被修改,它都需要更新一遍
        '''
        return redirect('/showlist/')

    return render(request,'edit.html',locals())

 4、删除数据

def delete(request):
    id=request.GET.get('dele_id')
    #删除数据
    models.User.objects.filter(id=id).delete()
    '''
    注意:现实中的删除数据并不会像上面这样把数据删除,而是通过加一个字段,来表示该数据是否删除
    '''
    return redirect('/showlist/')

 Django的ORM建立多表关系

关系字段参数

to:设置关联的表

to_field:设置要关联的表的字段

related_name

反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。

例如:

复制代码
复制代码
class Classes(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes")
复制代码
复制代码

当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:

models.Classes.objects.first().student_set.all()

当我们在ForeignKey字段中添加了参数 related_name 后,

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes", related_name="students")

当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:

models.Classes.objects.first().students.all()

related_query_name

反向查询操作时,使用的连接前缀,用于替换表名。

db_constraint

是否在数据库中创建外键约束,默认为True。

symmetrical

仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。

举个例子:

class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")

此时,person对象就没有person_set属性。

class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)

此时,person对象现在就可以使用person_set属性进行反向查询。

on_delete: 参数如下:

  • models.SET_NULL:置空模式,删除时,外键字段被设置为空,前提时该字段可以为空
  • models.CASCADE:级联删除
  • models.DO_NOTHING:删除数据的时候,不会删除到关联表的数据
  • models.PROTECT:删除关联数据的时候,会报错
  • models.SET_DEFAULT:删除子表数据的时候,外键字段设置为默认值
  • models.SET(值): 删除关联数据的时候,自定义一个值
#一对多
publish=models.ForeignKey(to='Publish')#to_field=这个参数是设置外键关联字段,不写默认为主键
#多对多
author=models.ManyToManyField(to='Author')
#一对一
authordetail=models.OneToOneField(to='Authordetail')
'''
注意:ManyToManyField会自动帮你创建一张表
         OneToOneField和ForeignKey会自动给字段加"_id"
         Django 1.x版本,是默认有级联的
'''
多表关系例子

内置类class Meta的使用

class BaseModel(models.Model):
    is_delete=models.BooleanField(default=False)
    #创建时间
    create_time=models.DateTimeField(auto_now_add=True)
    #更新时间
    update_time=models.DateTimeField(auto_now=True)
    class Meta:
        #抽象表,一般作为基类的时候使用,不再数据库建立出来
        abstract=True
        #用来指定model对应的数据库中的表名
        db_table='书表'
        #排序,加个“-”号就是降序
        ordering=['data']

verbose_name='xxx' #指定在admin管理界面中显示中文
verbose_name_plural='xxx' 表示复数形式的显示;中文的单数和复数一般不作区别。

 

多对多三种创建方式:

'''
第一种:全自动
优点:不用写代码,就可以自动建立第三张表,还支持orm提供操作第三张表的方法
缺点:不可以扩展,没办法添加额外字段
'''
class Book(models.Model):
    name=models.CharField(max_length=32)
    author = models.ManyToManyField(to='Author')

class Author(models.Model):
    name=models.CharField(max_length=32)

'''
第二种:手动
优点:扩展性高,可以根据自己的需求扩展字段
缺点:需要自己写代码,不支持orm提供的操作方法,比如:add、remove等
'''
class Book(models.Model):
    name=models.CharField(max_length=32)
class Author(models.Model):
    name=models.CharField(max_length=32)

class Book_Author(models.Model):
    book_id=models.ForeignKey(to='Book')
    author_id=models.ForeignKey(to='Author')
'''
第三种:半自动
优点:可以扩展字段、可以使用orm的正反向查询
缺点:不能使用orm提供的add,set,remove,clear方法
'''
class Book(models.Model):
    name=models.CharField(max_length=32)
    '''
    #参数解析:查作者的时候,是通过第三张表Book_Author中的book_id字段来获取author_id来查作者表
    through_fields中字段的顺序,第一个是放当前表的跟第三表关联的字段,第二个是放要查询的表关联的字段。
        比如:查书的作者,第一参数就是放book_id,第二个参数就是放author_id
    '''
    author=models.ManyToManyField(to='Author',through='Book_Author',through_fields=('book_id','author_id'))
class Author(models.Model):
    name=models.CharField(max_length=32)
class Book_Author(models.Model):
    book_id=models.ForeignKey(to='Book')
    author_id=models.ForeignKey(to='Author')

测试环境的搭建

#测试脚本.py文件
from django.test import TestCase
'''
测试环境的搭建
'''
import os
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "two_django.settings")
    import django
    django.setup()
    #测试代码
    '''
    注意:无论是模块还是测试代码,都只能放在这里
    '''
    from app

ORM操作

 十三个方法:

1、all():查询所有结果

2、fiter(**kwargs): 筛选条件符合的对象,例如:filter(name='helo',age='14')

3、get(**kwargs):返回所给筛选条件符合的对象,返回结果只有一个。返回结果超过一个,或者没有的时候,会报错,所以推荐使用filter()

4、exclude(**kwargs):筛选条件不匹配对象

5、values():凡回忆个ValueQuerySet对象,返回一个类似列表套字典的数据结构

6、values_list():跟values相似,返回的是一个列表套元组的形式

7、order_by():对查询结果排序

8、reverse():对查询结果反向排序

9、distinct():去重,注意不能出现主键字段,否则会无法去重

10、count():返回匹配的对象数量

11、first():返回第一条数据

12、last():返回最后一条数据

13、exists():判断是否存在    '''

    all方法:查询所有结果
    返回<QuerySet [<User: User object>, <User: User object>, <User: User object>]>
    '''
    r=models.User.objects.all()
    '''
    filter方法:获取符合筛选条件的结果
    返回结果:<QuerySet [<User: User object>]>
    注意:pk表示当前表的主键字段,filter括号内参数是and关系
    '''
    r=models.User.objects.filter(pk=3)

    '''
    #get方法:获取一个符合条件的对象
    返回结果:User object
    '''
    r=models.User.objects.get(pk=2)

    '''
    exclude方法:排除某个条件后的结果
    返回结果:<QuerySet [<User: User object>, <User: User object>]>
    '''
    r=models.User.objects.exclude(pk=2)

    '''
    #values方法:
    返回结果:<QuerySet [{'name': 'jk', 'passwd': '123'}, {'name': 'hello', 'passwd': '111'}, {'name': 'json', 'passwd': '123'}]>
    '''
    r=models.User.objects.values('name','passwd')
    '''
    value_list方法:
    返回结果是:<QuerySet [('jk', '123'), ('hello', '111'), ('json', '123')]>
    '''
    r=models.User.objects.values_list('name','passwd')

    '''
    orderby方法:对查询结果进行排序,默认是升序,降序只需要加个 - 就可以
    返回结果:<QuerySet [<User: User object>, <User: User object>, <User: User object>]>
    '''
    res=models.User.objects.order_by('age')
    res=models.User.objects.order_by('-age')  #加-号,变降序

    '''
    reverse方法:反向排序,前提时已经排序好的对象才可以使用该方法
    返回结果:<QuerySet [<User: User object>, <User: User object>, <User: User object>]>
    '''
    r=models.User.objects.order_by('age').reverse()

    '''
    distinct方法:去重
    返回结果:<QuerySet [{'name': 'jk'}, {'name': 'hello'}, {'name': 'json'}]>
    注意:不能有主键,而且必须是一模一样才可以去重
    '''
    r=models.User.objects.values('name').distinct()

    '''
    count方法:统计结果数量
    返回结果是 数量值 4
    '''
    r=models.User.objects.all().count()

    '''
    first、last方法:获取第一个和最后一个
    返回结果 User object
''' r=models.User.objects.all().first() print(r.name) r=models.User.objects.all().last() print(r.name) ''' exists方法:判断是否存在 返回结果是布尔值 ''' r=models.User.objects.filter(pk=2).exists()

 Django终端打印SQL语句

在settings.py文件中最后添加以下配置就可以

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

 双下划线查询

   #获取年龄大于21的用户
    r=models.User.objects.filter(age__gt=21)
    # 获取年龄大于等于21的用户
    r=models.User.objects.filter(age__gte=21)
    #获取名字有h的结果
    r=models.User.objects.filter(name__contains='h')   #区分大小写
    r=models.User.objects.filter(name__icontains='h')  #不区分大小写
    #获取年龄是18,20,23的用户
    r=models.User.objects.filter(age__in=[18,20,23])
    #获取age在19到21范围的用户
    r=models.User.objects.filter(age__range=[19,21])
    #获取注册时间是7月份的结果
    r=models.User.objects.filter(regist_time__month=7)
    r = models.User.objects.filter(regist_time__week_day__gte=3)
    #以h开头的结果
    r=models.User.objects.filter(name__startswith='h')   #区分大小写
    r = models.User.objects.filter(name__istartswith='h')  #不区分大小写
    #以0结尾的结果
    r = models.User.objects.filter(name__endswith='O')  #区分大小写
    r=models.User.objects.filter(name__iendswith='O')  #不区分大小写
单表查询之双下划线__例子

 外键的增删改查

    #多表增删改查
    #一对多
    #
    '''
    第一种:直接写实际字段publish_id,
    第二种,用虚拟字段publish ,要放对象
    '''
    models.Book.objects.create(name='好好说话',price=21,publish_id=1)
    p_object=models.Publish.objects.filter(pk=3).first()
    models.Book.objects.create(name='无线网络传感器',price=69,publish=p_object)
    #
    models.Publish.objects.filter(pk=3).delete()  #级联删除
    #
    models.Book.objects.filter(pk=2).update(publish_id='2')
    p_object=models.Book.objects.filter(pk=4).first()
    models.Book.objects.filter(pk=6).update(publish=p_object)
    #多对多
    #
    #add方法+id
    b_object=models.Book.objects.filter(pk=7).first()
    b_object.author.add(1,2)
    #add方法+对象
    a1=models.Author.objects.filter(pk=3).first()
    a2=models.Author.objects.filter(pk=5).first()
    a3=models.Author.objects.filter(pk=2).first()
    b_object=models.Book.objects.filter(pk=5).first()
    b_object.author.add(a2,a3)
    #删除
    book_object=models.Book.objects.filter(pk=3).first()
    book_object.author.remove(2)
    book_object.author.remove(a1,a2)
    #修改
    '''
    set方法:括号内放的是一个可迭代对象,该对象内可以放数字也可以放对象。
    '''
    book_object.author.set([1,3])
    book_object.author.set([a1,a2])
    #清空方法
    book_object.author.clear()
    '''
      总结:remove()、add()方法中括号内既可以传数字,也可以传对象
           set() 存放可迭代对象,对象内可以放数字跟对象
    '''

 正向方向概念:

正向:查外键所在的表就是正向  比如:知道书的id,查书的出版社

反向:查外键不在的表就是方向  比如:知道出版社,查出版社的书

口诀:正向查询用字段,反向查询用表名小写

    #查书籍id为4的出版社名字(正向)
    book_obj=models.Book.objects.filter(pk=4).first()
    # print(book_obj.publish.name)

   #查询书籍主键为5的作者
   book_obj=models.Book.objects.filter(pk=5).first()
    res=book_obj.author  #结果:app03.Author.None
    res=book_obj.author.all() #<QuerySet [<Author: Author object>, <Author: Author object>]>

    #查作者json号码
    author_obj=models.Author.objects.filter(name='json').first()
    res=author_obj.authordetail.iphone

    #查清华出版社出版的书(反向查找)
    publish_obj=models.Publish.objects.filter(name='清华出版社').first()
    res=publish_obj.book_set.all()

    #查作者alex写的书
    author_obj=models.Author.objects.filter(name='alex').first()
    res=author_obj.book_set.all()
  
    #查询作者年龄是19的作者名称
    authordetail_obj=models.AuthorDetail.objects.filter(age=19).first()
    res =authordetail_obj.author
    print(res.name)
    '''
    总结:反向查询:如果查询结果只有一个,则表名后面不需要加_set
                     如果查询结果有多个,则需要加_set.all
        正向查询:查询结果有多个要加all(),只有一个就不用加
        
        口诀,正向看字段,反向看表名小写。
    '''

 基于双下划线的跨表查询

  #查json的号码
    res=models.Author.objects.filter(name='json').values('authordetail__iphone')
    print(res)
    #反向
    res=models.AuthorDetail.objects.filter(author__name='json').values('iphone')
    print(res)
    #查书籍id=2的名字以及出版社名字
    res=models.Book.objects.filter(pk=2).values('name','publish__name')
    res=models.Publish.objects.filter(book__id=2).values('book__name','name')
    print(res)
    #查书籍id=3的作者名字
    res=models.Book.objects.filter(pk=3).values('author__name')
    res=models.Author.objects.filter(book__id=3).values('name')
    print(res)
    #查书籍id=3的作者的电话号码
    res=models.Book.objects.filter(pk=3).values('author__authordetail__iphone')
    print(res)

 聚合查询

  #聚合查询  关键字aggregate
    '''
        一般跟数据库有关的都在django.db.models可以找到
        找不到可以在django.db中找
    '''
    from django.db.models import Sum,Avg,Max,Min,Count
    res=models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Avg('price'),Count('pk'))
    print(res)

分组查询

    #分组查询  关键字:annotate
    #统计每一本书作者的个数
    res=models.Book.objects.annotate(author_num=Count('author')).values('name','author_num')
    print(res)
    #查询作者超过1个的书籍名字
    res=models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('name')
    print(res)
    #查看作者写过的书的总价格
    res=models.Author.objects.annotate(book_price=Sum('book__price')).values('name','book_price')
    print(res)
    #统计每个出版社最便宜的书的价格
    res=models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
    print(res)

F查询

主要用来解决两个字段之间的比较,实现动态比较

 #F查询:用来解决两个字段之间的比较
    from django.db.models import F
    #查看库存量<卖出的书
    res=models.Book.objects.filter(maichu__gt=F('kucun')).values('name')
    print(res)
    #将所有书籍的价格提高20元
    res=models.Book.objects.update(price=F('price')+20)
    '''
    F进行字符的拼接需要借助两个模块
    from django.db.models.functions import Concat
    from django.db.models import Value
    '''
    from django.db.models.functions import Concat
    from django.db.models import Value
    #给销量大于库存的书籍名字加一个爆款
    res=models.Book.objects.filter(maichu__gt=F('kucun')).update(name=Concat(F('name'),Value('爆款')))

Q查询

    from django.db.models import Q
    #查看价格大于100或者卖出大于一千的书籍名称
    '''
    filter参数是and关系,所以这里需要借助Q查询
    注意:| 表示or关系   ,和& 都表示and关系  
         ~表示取反,即not关系
    '''
    # res=models.Book.objects.filter(Q(price__gt=100)|Q(maichu__gt=1000)).values('name')
    res = models.Book.objects.filter(Q(price__gt=100) | ~Q(maichu__gt=1000)).values('name')
    print(res)
    '''
    Q的高阶玩法,将查询左边变成字符串,这样就可以应用到根据字段来筛选。
    '''
    q=Q()
    q.connector='or'  #默认是and关系
    q.children.append(('price__gt',100))
    q.children.append(('maichu__gt',1000))
    models.Book.objects.filter(q)

 事务

    from django.db import transaction
    #开启事务
    try:
        with transaction.atomic():
            #事务代码
    except Exception as e:
        print(e)
    print('其他操作')

数据库的优化

 1、only和defer

    '''
    all方法:当我没有使用到all方法返回的对象res的时候,
            它是不会走数据库执行的,只有用到才会走数据库 
    '''
    res=models.Book.objects.all()
    for i in res:
        print(i.name)
    '''
    only方法:获取only括号内的字段值的时候,是不会走数据库
            获取非only括号内的字段时候,才会走数据库
    defer方法:跟only方法相反,获取括号内字段会走数据库,
              非括号内的字段不会走数据库
    '''
    res=models.Book.objects.only('name')
    for i in res:
        # print(i.name)
        print(i.price)
    res=models.Book.objects.defer('name')
    for i in res:
        # print(i.name)
        print(i.price)
  

2、select_relate方法、prefetch_related方法

    '''
    select_relate方法:
        select_related会将Book表跟外键字段publish的Publish表关联起来,
        然后一次性将关联后表的所有数据都封装给返回的对象res中,
        这样res获取这两个表中任何一个字段的数据都不用再走数据库
        注意:select_related括号内只能放一对一或者一对多的外键字段,不能放多对多的外键字段
    prefetch_related方法:
        本质是子查询。会将子查询出来的结果封装在res对象中,给人一种一次性查询出来的感觉。
        当表比较大的时候,select_related的多表连表操作时速度会变慢,就可以用prefetch_related方法

''' res=models.Book.objects.select_related('publish') res=models.Book.objects.prefetch_related('publish') for i in res: print(i.publish.name)

总结:1、对于多对多字段和一对多字段,可以使用prefetch_related()来优化

           2、对于一对一和多对一的关系,可以使用select_related来优化

           3、select_related是通过减少SQL查询次数来进行优化和提高性能的

           4、prefetch_related优化是通过分别查询每个表,再用python来处理他们之间的关系。

    5、具体使用哪种优化最终还是得根据实际情况来选择。

Djangon ORM提供的批量插入方法:

def insert(requst):
    book_obj_list=[]
    for i in range(10000):
    #先创建book对象,并添加到list列表中
        book_obj=models.Book(name='第%s本书'%i)
        book_obj_list.append(book_obj)
    #将对象列表交给bulk_create处理
    models.Book.objects.bulk_create(book_obj_list)
    book_obj=models.Book.objects.all()
    return render(requst,'insert.html',locals())
posted @ 2020-10-05 20:44  NQ31  阅读(171)  评论(0编辑  收藏  举报