Django之ORM模型层

1、仿写 Django 中Settings.py的配置文件

  • Django配置文件的原理是:

    Django中其实是有两个settings.py文件.一个是系统默认的,一个是暴露给用户的.这样做的好处在于:一个专业名词叫可插拔式配置文件.

  • 可插拔式配置文件

    当用户配置了settings,那么系统就会加载用户的配置文件,反之,就加载系统默认的配置文件,这样就算用户不配置,系统也不会报错,基于这点,我们在今后的项目可以引入这一概念.

1.1设置可插拔式配置文件的思路.

读取用户的配置文件,其他就是读取settings.py中的参数,通常都是配置参数为大写,后面为参数值如下:

NAME='我是暴露给用户的配置文件'
  • 建立工程

1.2 代码实现.

----conf
	|----settings.py # 暴露给用户的配置文件
----lib
  |---conf
  	|----init.py   # 自动加载
    |----global_settings.py # 系统默认配置文件
start.py     # 启动文件
start.py
    import sys
    import os
    # 下面这两句话在pycharm中会自动帮我们将当前的环境路径,但是出了pycharm,其他的软件就不识别了。
    BASE_DIR=os.path.dirname(__file__)
    sys.path.append(BASE_DIR)
    # 设置环境变量默认为用户自定义的配置文件。
    os.environ.setdefault('xxx','conf.settings')
    from lib.conf import settings
    print(settings.NAME)
 conf.settings.py
		NAME="我是用户自定义的Django系统配置文件"
 lib.conf.global_settings.py
		NAME="我是默认的Django默认的系统配置文件"
 lib.conf.__init.py
			class Settings(object):
          def __init__(self):
              for setting in dir(global_settings):
                  if setting.isupper():
                      k=setting
                      # 从global_settings(对象)中获取k对用的值
                      v=getattr(global_settings,setting) # global_settings对象中取k的值
                      setattr(self,k,v) # self 是本类的对象. # 设置环境变量,k,v.
              # 从全局的大字典中获取到暴露给用户的配置文件路径
              mod=os.environ.get('xxx')
              # mod=conf.settings
              moudel=importlib.import_module(mod)
              # from conf import settings
              for setting in dir(moudel):
                  if setting.isupper():
                      k=setting
                      v=getattr(moudel,setting)
                      setattr(self,k,v)
# 循环获取默认的配置文件中所有的大写配置
# 利用setattr给对象不停的设置键值对
# 再循环获取暴露给用户的自定义配置文件中所有的大写的配置
# 再利用setattr给对象不停的设置键值对
settings=Settings()

2、orm基本使用

2.1 准备工作

2.1.1 新建一个py文件,用来测试

#添加manage.py的环境变量
import os


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day60.settings")
    # 导入django
    import django
    django.setup()
    # 引入models模块
    from app01 import models

2.1.2 后台打印对应的mysql语句。

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

2.2 单表查询基本使用

models.py
from django.db import models

# Create your models here.


class Books(models.Model):
    title=models.CharField(max_length=32)
    price=models.DecimalField(max_digits=8,decimal_places=2)
    date=models.DateField()
    
    def __str__(self): # 必须是str类型才会被调用
       return self.title
    
terminal>>>   python manage.py makemigration
terminal>>>  pyhton manage.py migrate
  

2.2.1 增

# 第一种
res=models.books.objects.create(title='三国演义',price=123.34,date='2019-08-09')
# 第二种
book_obj=models.books(title='三国演义',price=123.34,date='2019-08-09')
book_obj.save()

2.2.2 删

models.books.filter(pk=1).delete()

2.2.3 改

# 第一种
models.books.filter(pk=1).update(title='厚黑学')
# 第二种
book_obj=models.books.filter(pk=1).first()
book_obj.price=567.56
book_obh.save()

2.2.4 查(单表查询,必知必会13条)

2.2.4.1 查询所有 all
#  1、 查询所有  all()  QuerySet对象
res=models.Books.objects.all()
for i in res:
  print(i.title)

2.2.4.2 条件查询 filter
#  2、 条件查询  filter()  QuerySet对象
models.Books.objects.filter(pk=1)  

2.2.4.3 条件查询 get
# 3、条件查询 get  数据本身的对象. 查不到就报错,不推荐使用
res=models.Books.objects.get(title='论语')
print(res.price)

2.2.4.4 queryset对象的第一个 first
#4、 条件查询  first()  QuerySet对象
res=models.Books.objects.all()
print(res.first().title)

2.2.4.5 queryset对象的最后一个 last
# 5、 条件查询  last()  QuerySet对象
res = models.Books.objects.all()
print(res.last().title)

2.2.4.6 除……之外 exclude
# 6、 除什么之外  exclude()  QuerySet对象  后面可以一直加排除项目
res = models.Books.objects.all().exclude(pk=3)
# res = models.Books.objects.all().exclude(pk=3).exclude(pk=4)
for i in res:
  print(i.title)

2.2.4.7 列表套字典 values QuerySet
# 7、 列表套字典  values()  QuerySet对象
res = models.Books.objects.values()
    print(res)
    for i in res:
        print(i.get('title')) # 不能用句柄了.
<QuerySet [{'id': 3, 'title': '论语', 'price': Decimal('888.88'), 'date': datetime.date(2014, 5, 6)}, {'id': 4, 'title': '三国演义', 'price': Decimal('123.32'), 'date': datetime.date(2019, 7, 19)}, {'id': 5, 'title': '红楼梦', 'price': Decimal('56.23'), 'date': datetime.date(2022, 3, 4)}]>
论语
三国演义
红楼梦

2.2.4.8 列表套元祖 values_list
 # 8、 列表套元祖  values_list()  QuerySet对象
 res = models.Books.objects.values_list()
 print(res)
  
<QuerySet [(3, '论语', Dewqcimal('888.88'), datetime.date(2014, 5, 6)), (4, '三国演义', Decimal('123.32'), datetime.date(2019, 7, 19)), (5, '红楼梦', Decimal('56.23'), datetime.date(2022, 3, 4))]>

2.2.4.9 统计数据个数count
## 9、 count 统计数据的个数
count_res=models.Books.objects.all().count()
count_res1=models.Books.objects.count()
print(count_res,count_res1)
# 3,3 

2.2.4.10 去重 distinct
## 10、 distinct 去除重复
count_res = models.Books.objects.all().distinct()
count_res1=models.Books.objects.values('title','price').distinct()
print(count_res1)
<QuerySet [{'title': '论语', 'price': Decimal('888.88')}, {'title': '三国演义', 'price': Decimal('123.32')}, {'title': '红楼梦', 'price': Decimal('56.23')}]>

2.2.4.11 排序 order_by
## 11 order_by 排序
res=models.Books.objects.all().order_by('price') # 默认是升序
res1 = models.Books.objects.all().order_by('-price')  # 加负号降序
print(res,res1)

2.2.4.12 反序 reverse
 # 12.reverse()  前面必须是先结果排序才可以反转
  res = models.Books.objects.order_by('price').reverse()
  print(res)

2.2.4.13 查询是否存在 exists
 # 13.exists()  一点卵用没有 True or False
  res = models.Books.objects.filter(pk=3).exists()
  print(res)

2.3 神奇的双下划线

2.3.1 条件大于、小于、大于或等于

# 1、大于 __gt
  res=models.Books.objects.filter(price__gt=100)
  print(res)
# 2、小于 __lt
 	res=models.Books.objects.filter(price__lt=100)
  print(res)
# 3、大于等于 __gte
res=models.Books.objects.filter(price__gte=56.23)
  print(res)
# 4、小于等于 __lte
res=models.Books.objects.filter(price__lte=123.32)
print(res)

2.3.2 条件符合其中某一个

# 5、 符合某一个条件__in=[a,b,c,d]
res=models.Books.objects.filter(price__in=[123,123.32,56.32])
print(res)

2.3.3 条件符合在a和b之间

# 6、 在区间__range=(a,b)
res=models.Books.objects.filter(price__range=(123,888.88))# 顾前面的整数,不顾小数
print(res)

2.3.4 包含某个字符

# 包含字母P __contains
res=models.Books.objects.filter(title__contains='p') # 区分大小写
print(res)

res1=models.Books.objects.filter(title__icontains='P') # 不区分大小写
print(res1)

2.3.5 开头以某个字符

# __startswith='' 字符串
res1=models.Books.objects.filter(title__startswith='三') # 开头是三
res2=models.Books.objects.filter(title__endswith='p') # 开头是三
print(res1,res2)

2.3.5 查询日期

# __year __month 
res1=models.Books.objects.filter(date__year='2014') # 查询年份
res2=models.Books.objects.filter(date__month='08') # 查询月份
print(res1,res2)

2.4 多表查询

model.py
class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.DecimalField(max_digits=8,decimal_places=2)
    publish_date=models.DateField(auto_now=True)
    # 书和出版社 1对多
    publish=models.ForeignKey(to='Publish')
    # 书和作者 多对多
    authors=models.ManyToManyField(to='Author')

class Publish(models.Model):
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=64)

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    # 作者和作者细节 1对1 
    author_detail=models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    phone=models.BigIntegerField()
    addr=models.CharField(max_length=64)
    
    
    
python manage.py makemigrations
python manage.py migrate

2.4.1 1对多关系(基本操作)

2.4.1.1 添加数据(外键)
# publish_id 属于 虚拟字典
# publish_id直接传出版社主键值
models.Book.objects.create(title='三国演艺',price=123.23,publish_id=1) 

 # publish直接传出版社数据对象(这种比较常用)
publish_obj=models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='水浒传',price=234.45,publish=publish_obj)  

2.4.1.2 查询数据(外键)
##### 2.4.1.1 添加数据(外键)
# 2、查询
res=models.Book.objects.filter(pk=1).first()
print(res.publish)
print(res.publish_id)
# OUTPUT
东方出版社
1

2.4.1.3修改数据(外键)
# 3、修改
#publish_id直接传出版社主键值
# models.Book.objects.filter(pk=1).update(publish_id=3)

 # publish直接传出版社数据对象(这种比较常用)
publish_obj=models.Publish.objects.filter(pk=3).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)

2.4.1.4 删除数据(外键)
models.Publish.objects.filter(pk=3).delete()
# 默认也是级联更新 级联删除

2.4.2 多对多关系(基本操作)

2.4.2.1 增加 add
# add 可以添加数字,也可以添加对象.并且支持多个
# 给主键为3的书籍添加两个作者 3,4
book_obj=models.Book.objects.filter(pk=5).first()
#book_obj.authors.add(3)
book_obj.authors.add(4,6)
# 给主键为3的书籍添加两个作者对象 3,4
book_obj = models.Book.objects.filter(pk=5).first()
    author_obj=models.Author.objects.filter(pk=3).first()
    author_obj1=models.Author.objects.filter(pk=4).first()
book_obj.authors.add(author_obj,author_obj1)

2.4.2.2 改 set
# 2、改 可以添加数字,也可以添加对象,支持多个,如果修改的书籍已经存在了,就不会忽略.括号内对象必须可迭代.
# 修改主键为3的书籍,修改对象为4,6
book_obj=models.Book.objects.filter(pk=5).first()
book_obj.authors.set([4,6])
# 也可以传对象来修改。
book_obj = models.Book.objects.filter(pk=5).first()
author_obj = models.Author.objects.filter(pk=3).first()
author_obj1=models.Author.objects.filter(pk=4).first()
book_obj.auhtors.set(author_obj)
book_obj.authors.set((author_obj,author_obj1))

2.4.2.3 删除 remove
# 3、 删除 可以数字,可以对象.支持多个.
#book_obj=models.Book.objects.filter(pk=5).first()
#book_obj.authors.remove(3,4)
book_obj = models.Book.objects.filter(pk=5).first()
author_obj = models.Author.objects.filter(pk=3).first()
author_obj1=models.Author.objects.filter(pk=4).first()
book_obj.authors.remove(author_obj,author_obj1)

2.4.2.4 清空 clear
# 4、清空 所有关于主键为5的书所有的作者信息。 # clear不能有任何数据.
book_obj = models.Book.objects.filter(pk=5).first()
book_obj.authors.clear()

2.5 跨表查询

2.5.1 正反向查询

正向查询、反向查询

举个例子.a表和b表有关系,并且,外键在a,从a查b的数据,就是正向查询.从b查a的数据,就是反向查询.

两个查询的方向不同,用到的方法也有所不同.

# 1.查询书籍是python入门的出版社名称
book_obj=models.Book.objects.filter(title='python入门').first()
print(book_obj.publish.name)
print(book_obj.publish.addr)
# 2.查询书籍主键是6的作者姓名
book_obj = models.Book.objects.filter(pk=6).first()
print(book_obj.authors.all().first())
#3.查询作者是jason的手机号
author_obj=models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail.phone)
'''
    正向查询
    .对象
    如果查询如果超过1个,需要加all()
'''
# 4、查询出版社是机械出版社出版过的书籍
publish_obj=models.Publish.objects.filter(name='机械出版社').first()
print(publish_obj.book_set.all())
# 5.查询作者是jason写过的所有的书
author_obj=models.Author.objects.filter(name='tank').first()
print(author_obj.book_set.all())
# 6.查询手机号是19356615618的作者
author_detail_obj=models.AuthorDetail.objects.filter(phone=19356615618).first()
print(author_detail_obj.author.name)
'''
    反向查询
    .小写表名_set
    如果查询如果超过1个,需要加all()
    什么时候需要加_set
        当查询的结果可以是多个的情况下 需要加_set.all()
    什么时候不需要加_set
        当查询的结果有且只有一个的情况下 不需要加任何东西 直接表名小写即可
'''
# 题目中,从a查b那么,拿的对象就是a的.抓住题目.

2.5.2 双下划跨表查询

2.5.2.1 真正的正反向查询

若a和b表存在关系,那么orm对象中,拿a的对象去取b的值,则是正向.拿b的对象取b的值则是反向.

# 1.查询书籍是python入门的出版社名称
# 正向
res=models.Book.objects.filter(title='python入门').values('publish__name')
print(res)
# 反向
res1=models.Publish.objects.filter(book__title='python入门').values('name')
print(res1)
# 2.查询作者是jason的手机号码
# 正向
res=models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')
print(res)
# 反向
res1=models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
print(res1)

# 3.查询手机号是19356615618的作者姓名
# 正向
res=models.AuthorDetail.objects.filter(phone='19356615618').values('author__name')
print(res)
# # 反向
res1=models.Author.objects.filter(author_detail__phone='19356615618').values('name','age')
print(res1)

# 4.查询出版社是东方出版社出版的书籍名称
res=models.Publish.objects.filter(name='东方出版社').values('book__title')
print(res)
# # 反向
res1=models.Book.objects.filter(publish__name='东方出版社').values('title')
print(res1)
#
# # 5.查询作者是jason的写过的书的名字和价格
res=models.Author.objects.filter(name='jason').values('book__title','book__price')
print(res)
# # 反向
res1=models.Book.objects.filter(authors__name='jason').values('title','price')
print(res1)
#
# # 7.查询书籍是python入门的作者的手机号 从这author表跨到author_detail中.其中__ 
res=models.Book.objects.filter(title='python入门').values('authors__author_detail__phone')
# 反向
res=models.AuthorDetail.objects.filter(author__book__title='python入门').values('phone')
print(res)

2.6 聚和查询(函数) aggregate

from django.db.models import Max,Min,Sum,Avg,Count
res=models.Book.objects.all().aggregate(Avg("price"),Count('title'),Sum('price'),Max('price'),Min('price'))
#  res=models.Book.objects.aggregate(Avg("price"),Count('title'),Sum('price'),Max('price'),Min('price'))
print(res)
#{'price__avg': 90.22, 'title__count': 4, 'price__sum': Decimal('360.88'), 'price__max': Decimal('124.53'), 'price__min': Decimal('34.67')}

2.7 分组查询 annotate

相当于mysql的groupBy

# 1.统计每一本书的作者个数
   # 按书分组,从书找作者是正向 直接写book类中的authors对象
res=models.Book.objects.annotate(author_num=Count('authors')).values('author_num')
print(res)
# 2.统计出每个出版社卖的最便宜的书的价格
    # 按出版社分组,从出版社找书,反向的。book__price
res=models.Publish.objects.annotate(min_price=Min('book__price')).values('min_price')
print(res)
# 3.统计不止一个作者的图书
"""
1.统计每本书对应的作者个数
2.基于上面的结果 筛选出作者个数大于1 的
"""
res=models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('author_num')
print(res)
# 4.查询各个作者出的书的总价格
 # 作者查书
res=models.Author.objects.annotate(book_sum=Sum('book__price')).values('book_sum')
print(res)

2.8 F与Q查询

当我们要查询的条件不是自己手写的。而是取一个字段的所有值,需要用到F和Q

2.8.1 F查询

查询某一个字段

form django.db.models import F,Q
# 1、查询售出大于库存的书的名称
res=models.book.objects.filter(shouchu__gt=F('kucun'))
print(res)
# 2、将所有的书的价格 全部提高100块
res=models.Book.objects.update(price=F('price')+100)
print(res)
# 3、所有书的名称后面加爆款两个字
from django.db.Functions import Concat
from django.db.models import Values
res=models.Book.objects.update(name=Concat(F('title'),Vlalues('爆款')))

2.8.2 Q查询

需要 Or、And、Not条件关系

# 书名称叫python入门,或者价格等于224.53
# or
res=models.Book.objects.filter(Q(title='python入门') | Q(price='224.53'))
# and
res=models.Book.objects.filter(Q(title='python入门'),Q(price='224.53'))
# not
res=models.Book.objects.filter(~Q(title='python入门'),~Q(price='224.53'))
print(res)
# 需求变了,需要如果title 是用户手动输入的。传到后面判断,怎么操作?
 q=Q()
q.connector='or'
#q.children.append(('title','python入门'))
"""
字符串的左边 跟你的变量名条件书写一模一样
"""
q.children.append(('title__icontains','q')) # 注意是一个元组对象。也可以使用双下划线。
q.children.append(('maichu','666'))
res=models.Book.objects.filter(q)
print(res)

2.9 orm 常用字段

2.9.1、AutoField # 自增列

  • 必须填入 primarykey=true
    当model中如果没有自增列,则自动会创建一个列名为id的列。.如果用户写了,那么django就忽略.

2.9.2、CharField # 相当于mysql中的varchar类型

  • 必须要有max_length=number 参数. 指定长度

2.9.3 自定义Char类型.

class MyCharField(models.Field): # 1、继承models.Field
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length  # 2、必须指定长度
        super().__init__(max_length=max_length, *args, **kwargs) # 在调用父类的构造方法

    def db_type(self, connection): # 4、重写父类中的db_type方法
        return 'char(%s)' % self.max_length  # char(max_length)


class TestField(models.Model):
    name = MyCharField(max_length=16) # 直接用

2.9.4 IntegerField

  • 一个整数类型,范围在 -2147483648 to 2147483647大小内的10位整数

2.9.5 DateField

  • 日期格式 YYYY-MM-DD. 相当于Pyhton中的datetime.date()实例
  • auto_now_add=True 创建数据记录的时候会把当前时间添加到数据库
  • auto_now 每次更新数据记录的时候都会更新改字段

2.9.6 DateTimeField

  • 日期时间字段,格式 YYYY-MM-DD HH-MM[:ss.[.uuuuuu]][TZ],相当于Python中的datetime.datetime()
  • auto_now_add=True 创建数据记录的时候会把当前时间添加到数据库
  • auto_now 每次更新数据记录的时候都会更新改字段

2.9.7 BooleanField

  • bool
  • 该字段在存储数据的时候,只需要传布尔值,
  • 对应u数据库中的0/1

2.9.8 TextField

  • 对应mysql中的text

  • 文本类型.可以存大量的文本

2.9.9 EmailField

  • mysql Varchar(254)

  • 继承CharField

  • 存邮件

2.9.10 FileField

  • 字符串,路径保存在数据库,文件上传到指定路径

  • 参数:

    upload_to=" "上传文件的保存路径

    storage=None 存储组件 默认django.core.files.storage.FileSystemStorage

2.9.11 DecimalField(Field)

  • 10进制小数
  • 参数:
    max_digits,小数总长度
    decimal_places,小数位长度

2.10 字段参数说明

1、null 字段参数可以为空 null=True
2、unique 如果设置为unique=True 则该字段在此表中必须是唯一的 。
3、db_index 如果db_index=True 则代表着为此字段设置索引。
4、default  为该字段设置默认值。
ForeignKey
1、to  设置要关联的表
2、to_field 设置要关联的表的字段
3、on_delete  当删除关联表中的数据时,当前表与其关联的行的行为。

#####################
如果你使用的是django2.X版本 你在建数据库表关系的时候
你需要手动指定两个参数
(你要手动告诉django  级联更新 级联删除  是否建外键约束)

on_delete
db_constraint

2.11 查询优化(面试常问)

2.11.1 惰性查询

  • 不用不查,一用才查.
res=models.Book.objects.all() # 不用不查,用了才查。
发现sql语句没有打印
print(res) # 使用print后,就展示查询语句了.
# SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`maichu`, `app01_book`.`shouchu`, `app01_book`.`publish_id` FROM `app01_book` LIMIT 21; args=()

2.11.2 only

only 会将括号内的字段, 直接封装成对象, 点该字段,不需要走数据库

一旦你点了不是括号内的字段.就会频繁的走数据库查询.

# 需求1: 通过res.,拿到title
res=models.Book.objects.values("title")
for i in res:
  print(i.title)
# AttributeError: 'dict' object has no attribute 'title'  这种需要get.

res=models.Book.objects.only('title')
for i in res:
  print(i.title)
 # (0.044) SELECT `app01_book`.`id`, `app01_book`.`title` FROM `app01_book`; args=()
数据库只查询一次,并且直接返回对象.

  • 需求2 : 通过上面的案例,我们发现,数据库中只查询了title的字段.,尝试用i.price.看看数据库查几次?
res=models.Book.objects.only('title')
for i in res:
  print(i.price)
'''
(1.826) SELECT `app01_book`.`id`, `app01_book`.`price` FROM `app01_book` WHERE `app01_book`.`id` = 5; args=(5,)
223.23
178.45
(0.078) SELECT `app01_book`.`id`, `app01_book`.`price` FROM `app01_book` WHERE `app01_book`.`id` = 6; args=(6,)
(0.091) SELECT `app01_book`.`id`, `app01_book`.`price` FROM `app01_book` WHERE `app01_book`.`id` = 7; args=(7,)
224.53
(0.049) SELECT `app01_book`.`id`, `app01_book`.`price` FROM `app01_book` WHERE `app01_book`.`id` = 8; args=(8,)
134.67
'''
数据库执行了4次.

2.11.3 defer

和only相反

  • defer会将括号内的字段排除之外将其他字段对应的值 直接封装到返回给你的对象中 点该其他字段 不需要再走数据库
  • 一旦你点了括号内的字段 就会频繁的去走数据库查询
res=models.Book.objects.defer('title')
for r in res:
  print(r.price)
#(0.044) SELECT `app01_book`.`id`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`maichu`, `app01_book`.`shouchu`, `app01_book`.`publish_id` FROM `app01_book`; args=()
223.23
178.45
224.53
134.67

  • 自动连表操作

  • 两张表连起来后,所有的字段都放在对象中. 只查询一次.

  • select_related()括号中只能是外键.并且多对多字段不能放.只能放1对1和1对多

  • 如果括号内外建字段所关联的表中还有外键字段.还可以继续连表.

    select_related(外键字段__外键字段__外键字段...)

res=models.Book.objects.all()
for i in res:
	print(i.publish)
###########
	跨表查询会查询多次.

# book表中查出版社名称
res=models.Book.objects.select_related('publish')

for i in res:
  print(i.publish.name)
#############
机械出版社
机械出版社
工业出版社
机械出版社
(0.046) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`maichu`, `app01_book`.`shouchu`, `app01_book`.`publish_id`, `app01_publish`.`id`, `app01_publish`.`name`, `app01_publish`.`addr` FROM `app01_book` INNER JOIN `app01_publish` ON (`app01_book`.`publish_id` = `app01_publish`.`id`); args=()


res=models.Book.objects.select_related('authors')
for i in res:
  print(i.authors.name)
  ##############
', '.join(_get_field_choices()) or '(none)',
django.core.exceptions.FieldError: Invalid field name(s) given in select_related: 'authors'. Choices are: publish

  • 看似连表操作 其实是类似于子查
  • prefetch_related括号内只能放外键字段 多对多除外.
  • 并且多对多字段不能放
res=models.Book.objects.prefetch_related('publish')
for i in res:
  print(i.publish.name)
################ 先查Book.id,再拿Book.id去publish表中去查.最后塞到对象里面去.
(0.056) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`maichu`, `app01_book`.`shouchu`, `app01_book`.`publish_id` FROM `app01_book`; args=()
(0.055) SELECT `app01_publish`.`id`, `app01_publish`.`name`, `app01_publish`.`addr` FROM `app01_publish` WHERE `app01_publish`.`id` IN (4, 5); args=(4, 5)
机械出版社
机械出版社
工业出版社
机械出版社

2.11.6 select_related和prefetch_related两者之间的优劣

  • Select_related 内部自动连表 消耗的资源就在连表上 但是走数据库的次数较少
  • Prefetch_related 内部不做连表 消耗的资源就在查询次数上 但是给用户的感觉跟连表操作一样

2.12 Django orm事务提交

django orm中的事务操作
ACID
原子性
一致性
隔离性
持久性

         commit
    rollback
要么同时成功要么同时失败   
 # django 中如何开始事务
    from  django.db import  transaction
    with transaction.atomic():
        pass
        # 在该代码块中所写的orm语句 同属于一个事务

    # 缩进出来之后自动结束

posted @ 2023-06-14 21:23  FirstReed  阅读(17)  评论(0)    收藏  举报