• 四十五、ORM相关


    一 表查询数据准备及测试环境搭建

    1. 连接MySQL数据库

      提前创建好数据库

      数据库相关配置

      # settings.py
      
      DATABASES = {
          'default': {
          'ENGINE': 'django.db.backends.mysql',
          'NAME': 'day59',
          'USER': 'root',
          'PASSWORD': '123',
          'HOST': '127.0.0.1',
          'PORT': 3306,
          'CHARSET': 'utf8'
      	}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    2. 定义模型类

      # models.py
      from django.db import models
      
      # Create your models here.
      class User(models.Model):
          name = models.CharField(max_length=32, verbose_name='用户名')
          age = models.IntegerField(verbose_name='年龄')
          in_time = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      日期字段的参数auto_now是每次操作数据并保存的时候会自动更新当前时间,auto_now_add只有在创建数据时会自动添加当前时间,后续没有人为修改就不会改变。

    3. 执行数据库迁移命令

      python manage.py makemigrations
      python manage.py migrate
      
      • 1
      • 2
    4. 测试环境准备
      方式一:

      # tests.py
      from django.test import TestCase
      
      # Create your tests here.
      import os
      
      
      def main():
          """Run administrative tasks."""
          os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
          import django
          django.setup()
          from app01 import models
          # 插入数据
          models.User.objects.create(name='jasper', age=18)
          models.User.objects.create(name='jason', age=19)
          models.User.objects.create(name='jack', age=20)
          models.User.objects.create(name='tom', age=21)
          models.User.objects.create(name='lili', age=22)
      
      
      if __name__ == '__main__':
          main()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

      方式二:在pycharm的Python Consele控制台操作。

    二 ORM常见查询关键字

    1. all():
      返回当前 QuerySet (或 QuerySet 子类)的副本。

      print(models.User.objects.all())
      
      • 1

      结果:

      , , , , ]>
      
      • 1
    2. filter():filter(*args, **kwargs)

      返回一个新的 QuerySet,其中包含与给定查找参数相匹配的对象。

      print(models.User.objects.filter(pk=1))
      
      • 1

      结果:

      ]>
      
      • 1
    3. get():get(*args, **kwargs)

      返回与给定的查找参数相匹配的对象,你应该使用保证唯一的查询,比如主键或唯一约束中的字段。例如:

      print(models.User.objects.filter(pk=1))
      
      • 1

      结果:

      ]>
      
      • 1
    4. values():values(*fields, **expressions)

      返回一个 QuerySet,当用作可迭代对象时,返回字典,而不是模型实例。

      其中每一个字典都代表一个对象,键与模型对象的属性名相对应。

      print(models.User.objects.values('name'))
      
      • 1
      
      
      • 1
    5. values_list():values_list(*fields, flat=False, named=False)

      这与 values() 类似,只是在迭代时不返回字典,而是返回元组。每一个元组都包含了传入 values_list() 调用的各个字段或表达式的值——所以第一项是第一个字段。

      print(models.User.objects.values_list('name', 'age'))
      
      • 1
      
      
      • 1
    6. order_by():order_by(*fields)

      字段前面加负号表示降序。升序是隐含的。要随机排序,使用 “?”。

      print(models.User.objects.order_by('-age'))
      print(models.User.objects.order_by('?'))
      
      • 1
      • 2
      , , , , ]>
      , , , , ]>
      
      • 1
      • 2
    7. reverse():

      使用 reverse() 方法来反向返回查询集元素的顺序。第二次调用 reverse() 会将顺序恢复到正常方向。

      print(models.User.objects.order_by('-age').reverse())
      print(models.User.objects.order_by('-age').reverse().reverse())
      
      • 1
      • 2
      , , , , ]>
      , , , , ]>
      
      • 1
      • 2

      请注意,reverse() 一般只应在有定义顺序的 QuerySet 上调用(例如,当对定义了默认顺序的模型进行查询时,或者当使用 order_by() 时)。如果没有为一个给定的 QuerySet 定义这样的排序,对它调用 reverse() 没有实际效果(在调用 reverse() 之前,排序是未定义的,之后也将保持未定义)。

    8. distinct():distinct(*fields)

      返回一个新的 QuerySet,在其 SQL 查询中使用 SELECT DISTINCT。这将消除查询结果中的重复记录。

      models.User.objects.create(name='jasper', age=18)
      print(models.User.objects.values('name').distinct())
      
      • 1
      • 2
      
      
      • 1

      如果想用distinct的话,在distinct前面加上values或values_list

    9. count():

      返回一个整数,表示数据库中与 QuerySet 匹配的对象数量。

      print(models.User.objects.count())
      
      • 1
      6
      
      • 1

      count() 调用在幕后执行 SELECT COUNT(*),所以你应该总是使用 count() 而不是将所有的记录加载到 Python 对象中,然后在结果上调用 len() (除非你需要将对象加载到内存中,在这种情况下 len() 会更快)。

      请注意,如果你想知道 QuerySet 中的项数,并且也要从它中检索模型实例(例如,通过迭代它),那么使用 len(queryset) 可能更有效,因为它不会像 count() 那样引起额外的数据库查询

      如果查询集已经被完全检索到,count() 将使用该长度,而不是执行额外的数据库查询。

    10. first():

      返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 None。如果 QuerySet 没有定义排序,那么查询集自动按主键排序。

      print(models.User.objects.first())
      
      • 1
      User object (1)
      
      • 1
    11. last():

      与 first() 工作原理相同,但返回的是查询集中的最后一个对象。

    12. exists()

      如果 QuerySet 包含任何结果,则返回 True,如果不包含,则返回 False。

      print(models.User.objects.filter(name='xxx').exists())
      print(models.User.objects.filter(name='jasper').exists())
      
      • 1
      • 2
      False
      True
      
      • 1
      • 2
    13. exclude():exclude(*args, **kwargs)

      返回一个新的 QuerySet,其中包含与给定查找参数不匹配的对象。

      print(models.User.objects.exclude(name='jasper'))
      
      • 1
      , , , ]>
      
      • 1
    14. raw():raw(raw_query, params=(), translations=None, using=None)

      获取一个原始 SQL 查询,执行它,并返回一个 django.db.models.query.RawQuerySet 实例。这个 RawQuerySet 实例可以像普通的 QuerySet 一样进行迭代,提供对象实例。

      print(list(models.User.objects.raw("select * from app01_user")))
      
      • 1
      [, , , , , ]
      
      • 1

      使用原生sql的方式二:

      from django.db import connection
      cursor = connection.cursor()
      cursor.execute("select * from app01_user where id < 2")
      print(cursor.fetchall())
      
      • 1
      • 2
      • 3
      • 4
      ((1, 'jasper', 18, datetime.datetime(2022, 9, 5, 8, 32, 36, 835704)),)
      
      • 1

    总结:

    • 返回新的QuerySet的方法:
      all()
      filter()
      order_by()
      exclude()
      distinct()
      reverse()
      raw()
    • 返回特殊对象
      values() 返回一个列表包字典的查询集 可以迭代取值
      values_list() 返回一个列表包元组的查询集 可以迭代取值
    • 返回对象
      get()
      first()
      last()
    • 返回数字
      count()
    • 返回布尔值
      exists()

    官方文档:https://docs.djangoproject.com/zh-hans/4.0/ref/models/querysets/

    三 双下划线查询

    3.1 比较运算符

    print(models.User.objects.filter(id__gt=5))
    print(models.User.objects.filter(id__lt=5))
    print(models.User.objects.filter(id__gte=5))
    print(models.User.objects.filter(id__lte=5))
    
    • 1
    • 2
    • 3
    • 4
    ]>
    , , , ]>
    , ]>
    , , , , ]>
    
    • 1
    • 2
    • 3
    • 4

    3.2 成员运算符

    print(models.User.objects.filter(name__in=('jasper', 'jack')))
    
    • 1
    , , ]>
    
    • 1

    3.3 范围查询(数字)

    print(models.User.objects.filter(id__range=(1, 4)))
    
    • 1
    , , , ]>
    
    • 1

    3.4 模糊查询

    print(models.User.objects.filter(name__contains=('j')))
    
    • 1
    , , , ]>
    
    • 1

    3.5 日期处理

    print(models.User.objects.filter(in_time__day=5))
    print(models.User.objects.filter(in_time__month=8))
    
    • 1
    • 2
    , , , , , ]>
    
    
    • 1
    • 2

    四 查询ORM底层SQL语句

    方式一:如果要QuerySet对象,直接点query即可查看对应的sql语句。
    方式二:在setting.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',
            },
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    五 ORM外键字段创建

    # 创建表
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.FloatField()
        # 书籍和出版社是一对多关系 外键建立多的一方 需要指定on_delete参数
        publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
        # 数据和作者之间是多对多关系 外键建立在查询频率较高的一方
        auther = models.ManyToManyField(to='Auther')
    
    
    class Publish(models.Model):
        p_name = models.CharField(max_length=32)
    
    
    class Auther(models.Model):
        a_name = models.CharField(max_length=32)
        # 作者与作者详情表之间是一对一关系 外键字段建立在查询频率较高的一方 需要指定on_delete参数
        auther_info = models.OneToOneField(to='AutherInfo', on_delete=models.CASCADE)
    
    
    class AutherInfo(models.Model):
        age = models.IntegerField()
        address = models.CharField(max_length=32)
        phone_number = models.CharField(max_length=32)
    
    # 插入数据
    models.AutherInfo.objects.create(age=18, address='西安', phone_number='110')
    models.AutherInfo.objects.create(age=20, address='北京', phone_number='120')
    models.AutherInfo.objects.create(age=30, address='上海', phone_number='130')
    models.AutherInfo.objects.create(age=49, address='广州', phone_number='140')
    models.AutherInfo.objects.create(age=34, address='深圳', phone_number='150')
    models.Auther.objects.create(a_name='瑞文', auther_info_id=1)
    models.Auther.objects.create(a_name='剑姬', auther_info_id=5)
    models.Auther.objects.create(a_name='猴子', auther_info_id=3)
    models.Auther.objects.create(a_name='剑魔', auther_info_id=4)
    models.Auther.objects.create(a_name='瑟提', auther_info_id=2)
    models.Publish.objects.create(p_name='东方出版社')
    models.Publish.objects.create(p_name='西方出版社')
    models.Publish.objects.create(p_name='南方出版社')
    models.Publish.objects.create(p_name='北方出版社')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    六 外键字段数据操作

    add():

    # 书籍添加 外键值可以传真正的出版社id 也可以传出版社对象
    models.Book.objects.create(title='鲁滨逊漂流记', price=10.9, publish_id=1)
    models.Book.objects.create(title='牛虻', price=20.9, publish_id=3)
    pub_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='安徒生童话', price=30.9, publish=pub_obj)
    pub_obj1 = models.Publish.objects.filter(pk=4).first()
    models.Book.objects.create(title='我要做个好孩子', price=50.9, publish=pub_obj1)
    
    # 第三张虚拟表外键字段的添加 先求出书籍对象 用书籍对象点多对多外键的名字 就相当于进入第三张表中
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.auther.add(1)
    book_obj2 = models.Book.objects.filter(pk=2).first()
    book_obj2.auther.add(1, 2, 3)
    book_obj3 = models.Book.objects.filter(pk=3).first()
    book_obj3.auther.add(2, 4, 5)
    book_obj4 = models.Book.objects.filter(pk=4).first()
    book_obj4.auther.add(5)
    
    # 可以传集体的id值 也可以传对象 支持传多个
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    remove():

    book_obj = models.Book.objects.filter(pk=2).first()
    book_obj.auther.remove(2)
    
    # 可以传主键值 也可以传对象 也支持传多个 用逗号隔开
    
    • 1
    • 2
    • 3
    • 4

    set():

    book_obj = models.Book.objects.filter(pk=2).first()
    book_obj.auther.set((3, 4, 5)) 
    
    # set必须接收一个可迭代对象 可以是数字 也可以是对象 其实是删除加新增 
    
    • 1
    • 2
    • 3
    • 4

    clear():

    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.auther.clear()
    
    # 第三张表中清空关系
    
    • 1
    • 2
    • 3
    • 4

    七 正反向查询概念

    外键字段如果在我手上,我查你就是正向,比如由书名查出版社,外键字段在书这,所以是正向。反之则是反向。

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

    八 多表查询

    8.1 基于对象的跨表查询(子查询)

    # 1 查询书籍主键值为1的出版社
    # 1.1 由书籍主键值拿到书籍对象
    book_obj = models.Book.objects.filter(pk=1).first()
    # 1.2 判断正反向查询
    p_obj = book_obj.publish
    
    # 2 查询书籍主键值为2的作者
    book_obj = models.Book.objects.filter(pk=2).first()
    au_obj = book_obj.auther.all()
    # , , ]>
    
    # 3 查询作者猴子的地址
    au_obj = models.Auther.objects.filter(a_name='猴子').first()
    add = au_obj.auther_info.address  # 上海
    
    # 4. 查询出版社是东方出版社出版的书
    pub_obj = models.Publish.objects.filter(p_name='东方出版社').first()
    # 反向
    print(pub_obj.book_set.all())  # ]>
    
    # 5. 查询作者是剑姬写过的书
    au_obj = models.Auther.objects.filter(a_name='剑姬').first()
    print(au_obj.book_set.all())  # ]>
    
    # 查询手机号是130的作者
    au_info_obj = models.AutherInfo.objects.filter(phone_number='130').first()
    print(au_info_obj.auther)  # Auther object (3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    总结:正向时当查询的结果可能有多个时就需要加all(),如果是一个,则直接拿到对象。反向时当结果有多个时需要加_set.all(),只有一个就不需要加。

    8.2 基于上下划线的跨表查询(联表操作)

    # 基于双下划线的多表查询
    # 1 查询猴子的手机号
    # 正向
    res = models.Auther.objects.filter(a_name='猴子').values('auther_info__phone_number')
    print(res)  # 
    
    # 反向
    r_res = models.AutherInfo.objects.filter(auther__a_name='猴子').values('phone_number')
    print(r_res)
    
    # 2 查询书籍主键为2的出版社名和书名
    # 正向
    res1 = models.Book.objects.filter(pk=2).values('publish__p_name', 'title')
    print(res1)  # 
    
    # 反向
    r_res1 = models.Publish.objects.filter(book__pk=2).values('p_name', 'book__title')
    print(r_res1)
    
    # 3 查询书籍主键为3的作者姓名
    # 正向
    res2 = models.Book.objects.filter(pk=3).values('auther__a_name')
    print(res2)
    # < QuerySet[{'auther__a_name': '剑姬'}, {'auther__a_name': '剑魔'}, {'auther__a_name': '瑟提'}] >
    
    # 反向
    r_res2 = models.Auther.objects.filter(book__pk=3).values('a_name')
    print(r_res2)
    
    # 4 查询书籍主键是3的作者手机号
    res = models.Book.objects.filter(pk=3).values('auther__auther_info__phone_number')
    print(res)
    # 
    # {'auther__auther_info__phone_number': '140'}, {'auther__auther_info__phone_number': '120'}]>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    8.3 正反向查询进阶操作

    # 1.查询主键为1的书籍对应的出版社名称及书名
    # res = models.Publish.objects.filter(book__pk=1).values('p_name', 'book__title')
    # print(res)  # 
    
    # 2.查询主键为3的书籍对应的作者姓名及书名
    res = models.Auther.objects.filter(book__pk=3).values('a_name', 'book__title')
    
    # 3.查询剑姬的作者的电话号码和地址
    res = models.AutherInfo.objects.filter(auther__a_name='剑姬').values('phone_number', 'address')
    
    
    # 4.查询南方出版社出版的书籍名称和价格
    res = models.Book.objects.filter(publish__p_name='南方出版社').values('title', 'price')
    
    # 5.查询猴子写过的书的名称和价格
    res = models.Book.objects.filter(auther__a_name='猴子').values('title', 'price')
    
    # 6.查询电话是110的作者姓名和年龄
    res = models.Auther.objects.filter(auther_info__phone_number='110').values('a_name', 'auther_info__age')
    
    # 7.查询主键为2的书籍对应的作者电话号码
    res = models.Auther.objects.filter(book__pk=2).values('auther_info__phone_number')
    print(res)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    九 聚合查询

    聚合函数:max、min、sum、avg、count
    聚合查询:在没有分组前使用聚合函数需要关键字aggregate

    from django.db.models import Max, Min, Sum, Avg, Count
    
    res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
    
    print(res)  # {'price__max': 50.9, 'price__min': 10.9, 'price__sum': 113.6, 'price__avg': 28.4, 'pk__count': 4}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    十 分组查询

    分组之后,默认只能获取分组之后的字段,获取其他字段需要使用方法。

    我们也可以忽略掉此特性,将sql_mode中的only_full_group_by配置移除即可。

    按照整条数据分组:models.Book.objects.annotate()
    按照表中某个字段分组:models.Book.objects.values(‘name’).annotate()

    # 示例1:统计每一本书的作者个数
    res = models.Book.objects.annotate(count_num=Count('auther__pk')).values('count_num')
    print(res)  # 
    
    # 示例2:统计出每个出版社卖的最便宜的书的价格
    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('min_price', 'p_name')
    print(res)
    
    # 示例3:统计不止一个作者的图书
    res = models.Book.objects.annotate(num=Count('auther__pk')).filter(num__gt=1).values('title', 'num')
    print(res)
    
    # 示例4:查询各个作者出的书的总价格
    res = models.Auther.objects.annotate(num=Sum('book__price')).values('a_name', 'num')
    print(res)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    十一 F与Q查询

    F查询:查询的条件不是自定义的而是来自于表中的其他字段。

    # 手动book表中添加数据
    
    store = models.IntegerField(default=1)
    sale = models.IntegerField(default=1)
    
    • 1
    • 2
    • 3
    • 4
    # 1.查询库存数大于卖出数的书籍
    from django.db.models import F, Q
    res = models.Book.objects.filter(store__gt=F('sale')).values('title')
    print(res)
    
    # 2.将所有书籍的价格上涨1000块
    models.Book.objects.update(price=F('price') + 1000)
    
    # 3.将所有书籍名称加上爆款后缀
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.filter(pk=2).update(title=Concat(F('title'), Value('_爆款')))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Q查询:filter()等方法中逗号隔开的条件是AND关系。如果你需要进行更加复杂的查询(例如OR),你可以使用Q对象。

    from django.db.models import Q, F
    # 示例1: 查询卖出数大于100或者价格小于100块的
    res = models.Book.objects.filter(Q(sale__gt=100) | Q(price__lt=100)).values('title')
    print(res)
    
    # 查询 库存数是100 并且 卖出数不是0 的产品
    res = models.Book.objects.filter(Q(sale=100) & ~Q(store=0)).values('title')
    print(res)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。

    同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。

    十二 ORM查询优化

    12.1 only和defer

    Django ORM默认是惰性查询,当ORM语句在后续的代码中真正需要使用的时候才会执行。

    如果你明确不需要这个数据库列(或在大部分情况里不需要),使用 defer() 和 only() 来避免加载它们。

    obj = models.Book.objects.only('title', 'price')
    for i in obj:
        print(i.title)
        print(i.price)
        print(i.sale
    
    • 1
    • 2
    • 3
    • 4
    • 5

    only会查询括号内的字段并封装成一个对象返回,使用该对象不会在通过数据库查询。但是查询括号里没有的字段,还是会走数据库。

    obj = models.Book.objects.defer('title', 'price')
    for i in obj:
    	print(i.title)
    	print(i.price)
    	print(i.sale)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    而defer和only相反。

    12.2 select_related和prefetch_related

    select_related()

    select_related(*fields)
    返回一个 QuerySet,它将“跟随”外键关系,在执行查询时选择额外的相关对象数据。这是一个性能提升器,它导致一个更复杂的单一查询,但意味着以后使用外键关系将不需要数据库查询。

    b = models.Book.objects.select_related('publish') # 会拼表
    for i in b:
    	print(i.title)
    	print(i.price)
    	print(i.sale)
    c = models.Auther.objects.select_related('auther_info')
    print(c)  # 只支持一对一和一对多关系
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    prefetch_related()
    prefetch_related(*lookups)

    返回一个 QuerySet,它将在一个批次中自动检索每个指定查询的相关对象。

    这与 select_related 有类似的目的,二者都是为了阻止因访问相关对象而引起的数据库查询潮,但策略却完全不同。

    select_related 的工作方式是创建一个 SQL 连接,并在 SELECT 语句中包含相关对象的字段。出于这个原因,select_related 在同一个数据库查询中得到相关对象。然而,为了避免因跨越“many”关系进行连接而产生更大的结果集,select_related 仅限于单值关系——外键和一对一。

    prefetch_related 则对每个关系进行单独的查找,并在 Python 中进行“joining”。这使得它除了支持 select_related 的外键和一对一关系外,还可以预取多对多和多对一的对象,这是用 select_related 无法做到的。

    https://docs.djangoproject.com/zh-hans/4.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related

    十三 事务操作

    Django 默认的事务行为:Django 默认的事务行为是自动提交。除非事务正在执行,每个查询将会马上自动提交到数据库。

    Django 自动使用事务或还原点,以确保需多次查询的 ORM 操作的一致性,特别是 delete() 和 update() 操作。

    官方文档:https://docs.djangoproject.com/zh-hans/4.1/topics/db/transactions/#deactivating-transaction-management

    十四 模型层常见字段

    AutoField  一个 IntegerField,根据可用的 ID 自动递增。你通常不需要直接使用它;如果你没有指定,主键字段会自动添加到你的模型中
    BigAutoField  一个 64 位整数,与 AutoField 很相似,但保证适合 1 到 9223372036854775807 的数字。
    BigIntegerField  一个 64 位的整数,和 IntegerField 很像,只是它保证适合从 -9223372036854775808 到 9223372036854775807 的数字。该字段的默认表单部件是一个 NumberInput。
    BinaryField  一个用于存储原始二进制数据的字段。
    BooleanField  一个 true/false 字段。
    CharField  一个字符串字段,适用于小到大的字符串。
    DateField  一个日期,在 Python 中用一个 datetime.date 实例表示。有一些额外的、可选的参数。
    DateTimeField  一个日期和时间,在 Python 中用一个 datetime.datetime 实例表示。与 DateField 一样,使用相同的额外参数。
    DecimalField  一个固定精度的十进制数,在 Python 中用一个 Decimal 实例来表示。它使用 DecimalValidator 验证输入。
    DurationField  一个用于存储时间段的字段——在 Python 中用 timedelta 建模。当在 PostgreSQL 上使用时,使用的数据类型是 interval,在 Oracle 上使用的数据类型是 INTERVAL DAY(9) TO SECOND(6)。否则使用微秒的 bigint。
    EmailField  一个 CharField,使用 EmailValidator 来检查该值是否为有效的电子邮件地址。
    FileField  一个文件上传字段
    FilePathField  一个 CharField,其选择仅限于文件系统中某个目录下的文件名。有一些特殊的参数,其中第一个参数是 必须的。
    FloatField  在 Python 中用一个 float 实例表示的浮点数。
    IntegerField  一个整数。从 -2147483648 到 2147483647 的值在 Django 支持的所有数据库中都是安全的。
    JSONField  一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和 None。
    PositiveBigIntegerField  就像一个 PositiveIntegerField,但只允许在某一特定点下的值(依赖于数据库)。0 到 9223372036854775807 的值在 Django 支持的所有数据库中都是安全的。
    PositiveIntegerField  就像 IntegerField 一样,但必须是正值或零( 0 )。从 0 到 2147483647 的值在 Django 支持的所有数据库中都是安全的。出于向后兼容的原因,接受 0 的值。
    PositiveSmallIntegerField  就像一个 PositiveIntegerField,但只允许在某一特定(数据库依赖的)点下取值。0 到 32767 的值在 Django 支持的所有数据库中都是安全的。
    SlugField  Slug 是一个报纸术语。slug 是一个简短的标签,只包含字母、数字、下划线或连字符。它们一般用于 URL 中。
    SmallAutoField  就像一个 AutoField,但只允许值在一定(依赖于数据库)的限制下。1 到 32767 的值在 Django 支持的所有数据库中都是安全的。
    SmallIntegerField  就像一个 IntegerField,但只允许在某一特定(依赖于数据库的)点下取值。从 -32768 到 32767 的值在 Django 支持的所有数据库中都是安全的
    TextField  一个大的文本字段。该字段的默认表单部件是一个 Textarea。
    TimeField  一个时间,在 Python 中用 datetime.time 实例表示。接受与 DateField 相同的自动填充选项。
    URLField  该字段的默认表单部件是一个 URLInput。
    UUIDField  一个用于存储通用唯一标识符的字段。使用 Python 的 UUID 类。当在 PostgreSQL 上使用时,它存储在一个 uuid 的数据类型中,否则存储在一个 char(32) 中。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    十五 ORM常见字段参数

    以下参数对所以字段类型均有效,且是可选的。

    1. null
      如果是 True, Django 将在数据库中存储空值为 NULL。默认为 False。
    2. black
      如果是 True ,该字段允许为空。默认为 False 。
    3. choices
      当字段数据类型的可能性是可以完全列举出来的时候,应该考虑该参数。
      class UserInfo(models.Model):
          username = models.CharField(max_length=32)
          gender_choice = (
              (1, '男性'),
              (2, '女性'),
          )
          gender = models.IntegerField(choices=gender_choice)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      查询:models.UserInfo.objects.filter(pk=1).first().get_gender_display()
    4. db_index
      如果是 True,将为该字段创建数据库索引。
    5. default
      该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
    6. primary_key
      如果设置为 True ,将该字段设置为该模型的主键。
    7. unique
      如果设置为 True,这个字段必须在整个表中保持值唯一。
    8. unique_for_date
      将其设置为 DateField 或 DateTimeField 的名称,要求该字段的日期字段值是唯一的。
    9. verbose_name
      字段的一个人类可读名称,如果没有给定详细名称,Django 会使用字段的属性名自动创建,并将下划线转换为空格。

    关系字段:
    on_delete:

    • CASCADE:级联删除。Django 模拟了 SQL 约束 ON DELETE CASCADE 的行为,也删除了包含 ForeignKey 的对象
    • PROTECT:当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
    • SET_NULL:设置 ForeignKey 为空;只有当 null 为 True 时,才有可能。
    • SET_DEFAULT:将 ForeignKey 设置为默认值,必须为 ForeignKey 设置一个默认值。
    • DO_NOTHING:不采取任何行动。如果你的数据库后端强制执行引用完整性,这将导致一个 IntegrityError 除非你手动添加一个 SQL ON DELETE 约束条件到数据库字段。
    • SET():当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数。

    官方文档:https://docs.djangoproject.com/zh-hans/4.1/ref/models/fields/#module-django.db.models.fields

    十六 多对多三种创建方式

    • 自动创建
      authors = models.ManyToManyField(to=“Author”)
      优点:自动创建第三张虚拟表。
      缺点:第三张表扩张性查。
    • 手动创建
      class Book(models.Model):
      	pass
      
      class Author(models.Model):
      	pass
      
      class Book2Author(models.Model):
      	book_id = models.ForeignKey(to="Book")
      	author_id = models.ForeognKey(to="Author")
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      优点:第三张表扩张性强。
      缺点:不能使用反向查询以及多对多关系的的方法(add remove set clear)
    • 半自动创建
      class Book(models.Model):
          authors = models.ForeignKey(to='Author', through='Book2Author', through_fields=('book_id', 'author_id'))
      
      
      class Author(models.Model):
          pass
      
      
      class Book2Author(models.Model):
          book_id = models.ForeignKey(to="Book")
          author_id = models.ForeignKey(to="Author")
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
  • 相关阅读:
    springboot2.3.7升级到springboot2.7.2
    LibreCAD windows 编译
    PHP安全问题:远程溢出、DoS、safe_mode绕过漏洞
    阿里开源玄铁RISC-V系列处理器,推动RISC-V架构走向成熟
    计算机网络入门基础篇——运输层
    通过 http-server 运行刚打包出来的脚手架项目
    有哪些设计模式,作用是什么?
    独家 | 放弃Jupyter Notebooks吧,教你如何用仪表板展示研究成果
    tornado无验证demo
    视频模板SDK,为企业带来无限创意与效率
  • 原文地址:https://blog.csdn.net/weixin_68531269/article/details/126707561