五、Django之模型层

Django之模型层

1、单表操作

  1. 注意

    • Django自带的sqlite3数据库对时间格式不是很敏感,处理的时候容易出错

      """
      更换数据库
      	1.先在settings.py中配置
          DATABASES = {
              'default': {
                  'ENGINE':'django.db.backends.mysql',
                  'NAME': 'day60',
                  'USER':'root',
                  'PASSWORD':'123456',
                  'HOST':'127.0.0.1',
                  'PORT':3306,
                  'CHARSET':'utf8'
              }
          }
      	2.再到任意一个init.py中声明
          import pymysql
      	pymysql.install_as_MySQLdb()
      """
      
    • Django不支持单文件执行,可在应用文件下随便创建一个py文件写入下面代码

      # 在manage.py中拷走以下代码
      import os
      
      if __name__ == '__main__':
          os.environ.setdefault('DJANGO_SETTINGS_MODULE','day64.settings')
          
          import django
          django.setup()
      
  2. 单表具体操作

    # 增加数据
    # 1.直接创建一个数据对象
    	res = models.User.objects.creater(name='jason',age=18,register_time='2020-1-1')
        print(res)
    # 2.用类实例化对象,并保存
    	import datetime
        ctime = datetime.datetime.now()
        user_obj = models.User(name='alex',age=18,register_time=ctime)
        user_obj.save()
        
        
    # 删除数据
    # 1.用filter筛选得到的结果都是queryset对象 存放一个一个的数据对象
    # pk映射主键字段
    	res = modles.User.objects.filter(pk=2)
        print(res)
        res.delete()
    # 2.找到具体的数据对象并删除
    	user_obj = models.User.objects.filter(pk=1).first().delete()
        prinnt(user_obj)
        
        
    # 修改数据
    	models.User.objects.filter(pk=1).update(name='jason')
        
    """
    总结
    	通过filter获得对象是queryset对象,可以delete,可以update,都是对这个对象内的所有元素统一进行
    	操作filter筛选过后用first得到的是对象中的第一个数据对象,数据对象也同样可以delete,update如果
    	把filter换成get会直接得到数据对象,但是这种方法如果找不到就会直接报错,不推荐使用
    """
    

2、必知必会13条

# 1.all() 查询所有数据
res = models.User.objects.all()

# 2.filter() 带有过滤条件的查询
res = models.User.objects.filter(pk=1)

# 3.get() 直接拿数据对象 但是当条件不存在直接报错
res = models.User.objects.get(pk=1)

# 4.first()  那过滤后第一条数据对象
res = models.User.objects.filter(pk=1).first()

# 5.last()  那过滤后最后一条数据对象
res = models.User.objects.filter(pk=1).last()

# 6.values() 指定获取数据字段 列表套字典
res = models.User.objects.values('name','age')
<QuerySet [{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]>

# 7.values_list() 指定获取数据字段 列表套元组
res = models.User.objects.values_list('name','age')
<QuerySet [('jason', 18), ('egon', 84)]>

# 8.distinct() 去重 一定要刨去主键
res = models.User.objects.values('name','age').distinct()

# 9.order_by() 排序 
res = models.User.objects.order_by('age')  # 升序
res = models.User.objects.order_by('-age') # 降序

# 10.reverse() 反转 前提是数据已经排过序
res = models.User.objects.order_by('age').reverse()

# 11.count() 统计当前数据个数
res = models.User.objects.count()

# 12.exclude() 排除在外
res = models.User.objects.exculde(name='jason')

# 13.exists() 判断数据是否存在
res = models.User.objects.filter(pk=10).exists()

3、查看内部的SQL语句方法

  1. 方式一:在queryset对象后面.query

  2. 方式二:在配置文件中配置以下代码

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

4、神奇的双下划线查询

# 1.年龄大于35岁的数据
res = models.User.objects.filter(age__gt=35)

# 2.年龄小于35岁的数据
res = models.User.objects.filter(age__lt=35)

# 3.大于等于 小于等于
res = models.User.objects.filter(age__gte=32)  # 大于等于
res = models.User.objects.filter(age__lte=32)  # 小于等于

# 4.年龄是18 或者32 或者40的数据
res = models.User.objects.filter(age__in=[18,32,40])

# 5.年龄在18到40岁之间,首尾全要
res = models.User.objects.filter(age__range[18,40])

# 6.查询名字里面含有s的数据  模糊查询 区分大小写
res = models.User.objects.filter(name__contains='s')

# 7.忽略大小写
res = models.User.objects.filter(name__icontains='s')

# 8.以某个字符开头或以某个字符结尾
res = models.User.objects.filter(name__startwith='j') # 开头
res = models.User.objects.filter(name__endwith='o')   # 结尾

# 9.查询出注册时间是2020年1月
res = models.User.objects.filter(register_time__year='2020')  # 2020
res = models.User.objects.filter(register_time__month='1')    # 1

5、一对多外键增删查改

# 增加数据 create()
# 1.直接写实际字段
	models.Book.objects.create(title='西游记',price=19.9,publish_id=1)
# 2.虚拟字段(对象),直接写外键表名,一对一的外键关系时使用
	publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.creater(title='三国演义',price=20.1,publish=publish_obj)
    
# 删除数据 delete()
# 1.X中删除就是常规删除 因为会级联删除所以不用考虑其他表
	models.Pulish.objects.filter(pk=4).delete()

# 修改数据 update()
# 1.直接写字段名
	models.Book.objects.filter(pk=4).update(publish_id=2)
# 2.虚拟字段(对象),直接写外键表名
    publish_obj = models.Pulish.objects.filter(pk=2).first()
    models.Book.objects.filter(pk=5).update(pulish=publish_obj)
    
"""
总结
	在多表关系时,修改外键字段可放主键id值,也可以放具体的外键对象
"""

6、多对多外键的增删查改

# 增加数据 add()
book_obj = models.Book.objects.filter(pk=4).first()
boob_obj.author.add(1,2)
# 可以传单个也可以传多个 这里的1,2是指添加的author的id 所以也可以添加具体的author对象

# 删除数据 remove()
book_obj = models.Book.objectes.filter(title='三国演义').first()
author_obj = models.Author.objects.filter(name='jason').first()
book_obj.authors.remove(author_obj)
# 删除同样也可以传主键id值 和具体的对象 支持多个

# 修改数据 set()
book_obj = models.Book.objects.filter(title='西游记').first()
book_obj.authors.set((1,2))
# 括号内反的是可迭代对象
    

# 清空 在第三张表中清空某个书籍与作者的绑定关系
book_obj = models.Book.objects.filter(title='西游记').first()
book_obj.clear()
# clear()括号内不要加任何参数

"""
总结
	1.多对多操作先要获取一个数据对象,然后.外键字段名不需要加_id
    2.添加时,add可以传多个值,可id,可对象
    3.删除时,remove可以传多个值,可id,可对象
    4.修改时,set可以传多个值,可id,可对象,已有的不会变,增加新添的
    5.清空时,clear不需要传任何值
"""

7、正反查询概念

  1. 正向:外键字段在我这里,我查你,就是正向

  2. 反向:外键字符在你这里,我查你,就是反向

  3. 例子:book和publish,外键字段在book表中

    • 由book查publish就是正向
    • 由publish查book就是反向
  4. 正向查询按字段,获取多个.all()

    book_obj = models.Book.objects.filter(pk=2).first()
    author_obj = book_obj.authors.all() # 跳转到作者表拿全部
    print(author_obj)
    
  5. 反向查询按小写表名_set,获取多个__set.all()

    author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    author_obj = author_detail_obj.author_set # 获取具体的数据对象
    print(author_obj.name)
    
    author_obj = models.Author.objects.filter(name='jason').first()
    book_obj = author_obj.book_set # app01.Book.None
    book_obj = author_obj.book_set.all() # <QuerySet [<Book: Book object>, <Book: Book object>]>
    print(book_obj)
    

8、多表查询

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

"""正向查询"""
# 1.查询数据主键为1的出版社
	book_obj = models.Book.objects.filter(pk=1).first()
    res = book_obj.pulish # 主键为1的出版社对象
    res.name # 主键为1的出版社对象的名字
    res.addr # 主键为1的出版社对象的地址
    
# 2.查询数据主键为2的作者
	book_obj = models.Book.objects.filter(pk=2).first()
    res = book_obj.authors # app01.Author.None
    res = book_obj.authors.all() # <QuerySet [<Author: Author object>, <Author: Author object>]>
    
# 3.查询作者jason的电话号码
	author_obj = models.Author.objects.filter(name='jason').first()
    res = author_obj.author_detail # 跳转到作者详情表
    res.phone # jason的号码
    res.addr  # jason的地址
    
"""
正向查询总结:
	在书写orm语句的时候跟写sql语句一样的
    不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点
    
    正向什么时候需要加.all()
        当你的结果可能有多个的时候就需要加.all()
        	book_obj.authors.all()
        如果是一个则直接拿到数据对象
            book_obj.publish
            author_obj.author_detail
"""
"""反向查询"""
# 1.查询出版社是东方出版社出版的书
	publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    res = publish_obj.book_set  # app01.Book.None
    res = publish_obj.book_set.all() # <QuerySet [<Author: Author object>, <Author: Author object>]>

# 2.查询作者是jason的书
	author_obj = models.Author.objects.filter(name='jason').filter()
    res = author_obj.book_set # app01.Book.None
    res = author_obj.book_set.all() # <QuerySet [<Author: Author object>, <Author: Author object>]>
    
# 3.查询手机号是110的作者姓名
	author_detail_obj = models.AuthoeDetail.objects.filter(phone=110).first()
    res = author_detail_obj.authors # 跳转到作者表
    res.name # 手机号是110的作者姓名
    
    
"""
反向查询总结
	表名小写加_set
        当你的查询结果可以有多个的时候 就必须加.all()
        当你的结果只有一个的时候 不需要加.all()
"""

②、联表查询(基于双下划线的跨表查询)

# 1.查询jason的手机号码和作者姓名
	# 正向
    res = models.Authors.objects.filter(name='jason').values('author_detail__phone','name')
    # 反向
    res = models.AuthorDetail.objects.filter(author_name='jason') # 拿作者姓名是jason的作者详情
    res = models.AuthorDetail.objects.filter(author__name='jason').values('author_name','phone')
    
# 2.查询书籍主键为1的出版社名称和书的名称
	# 正向
    res = models.Book.onjects.filter(pk=1).values('publish__name','title')
    # 反向
    res = models.Publish.objects.filter(book__id=1).values('name','book__title')
    
# 3.查询书籍主键为1的作者姓名
	# 正向
    res = models.Book.objects.filter(pk=1).values('authors__name')
    # 反向
    res = models.Authors.objects.filter(book_id=1).values('name')
    
# 4.查询书籍主键为1的作者的手机号
	# 正向
    res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
    # 反向
    res = models.Author.objects.filter(book__id=1).values('author_detail__phone')
"""
总结:
    你只要掌握了正反向的概念
    以及双下划线
    那么你就可以无限制的跨表
"""

③、多表查询题目汇总

# 子查询
    # 1.查询书籍主键为1的出版社的名字和地址
    # book_obj = models.Book.objects.filter(pk=1).first()
    # publish_obj = book_obj.publishs # 跳转到出版社表
    # publish_name = publish_obj.name
    # publish_addr = publish_obj.addr
    # print(publish_name)
    # print(publish_addr)

    # 2.查询书籍主键为2的作者
    # book_obj = models.Book.objects.filter(pk=2).first()
    # author_obj = book_obj.authors.all() # 跳转到作者表拿全部
    # print(author_obj)

    # 3.查询作者jason的电话号码
    # author_obj = models.Author.objects.filter(name='jason').first()
    # author_detail = author_obj.author_detail # 跳转到作者详情表
    # print(author_detail.phone)

    # 4.查询出版社是东方出版社出版的书
    # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    # book_obj = publish_obj.book_set # app01.Book.None
    # book_obj = publish_obj.book_set.all() # <QuerySet [<Book: Book object>, <Book: Book object>]>
    # print(book_obj)

    # 5.查询作者是jason的书
    # author_obj = models.Author.objects.filter(name='jason').first()
    # book_obj = author_obj.book_set # app01.Book.None
    # book_obj = author_obj.book_set.all() # <QuerySet [<Book: Book object>, <Book: Book object>]>
    # print(book_obj)

    # 6.查询手机号码是110的作者姓名
    # author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    # author_obj = author_detail_obj.author
    # print(author_obj.name)


# 联表查询
    # 1.查询jason的手机号码和作者姓名
    # 正向
    # res = models.Author.objects.filter(name='jason').values('name','author_detail__phone')
    # print(res)
    # 反向
    # res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name')
    # print(res)

    # 2.查询书籍主键为1的出版社的名称和书的名称
    # 正向
    # res = models.Book.objects.filter(pk=1).values('publishs__name','title')
    # print(res)
    # 反向
    # res = models.Publish.objects.filter(book__id=1).values('name','book__title')
    # print(res)

    # 3.查询书籍主键为1的作者姓名
    # 正向
    # res = models.Book.objects.filter(pk=1).values('authors__name')
    # print(res)
    # 反向
    # res = models.Author.objects.filter(book__pk=1).values('name')
    # print(res)

    # 4.查询书籍主键为1的作者的手机号
    # 正向
    # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
    # print(res)
    # 反向
    # res = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
    # print(res)

9、聚合分组查询

①、聚合查询(aggregate)

PS:通常情况下聚合查询都是配合物分组查询一起使用的

# django中与数据库相关的模块基本都是在django.db.models里或是django.db里
from django.db.models import Max,Min,Sum,Count,Avg
  
# 查询所有书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
# 查询所有书的最高价
res = models.Book.objects.aggregate(Max('price'))
# 查询所有书的最低价
res = models.Book.objects.aggregate(Min('price'))
# 查询所有书的总价
res = models.Book.objects.aggregate(Sum('price'))
# 查询总共有多少本书
res = models.Book.objects.aggregate(Count('pk'))

# 也可以联合使用
res=models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))

②、分组查询(annotate)

PS:分组在mysql中类似于group_by,分组后默认只能拿到分组的数据。如果报错,可能是严格模式的问题,重新设置一下ONLY_FULL_GROUP_BY即可

# 1.统计每个作者写了几本书
res = models.Author.objects.annotate(count_book=Count('book__id')).values('name','count_book')
# 2.统计每本书有几个作者
res = models.Book.objects.annotate(count_author=Count('authors')).values('title','count_author')
# 3.统计每个出版社最便宜的书
res = models.Publish.objects.annotate(cheap_book_price=Min('book__price')).values('title','cheap_book_price')
# 4.统计作者不止1个的书籍
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')
# 5.统计每个作者写的书的总价格
res = models.Author.objects.annotate(sum_book_price=Sum('book__price')).values('name','sum_book_price')

10、F与Q查询

①、F查询

  1. F能够帮助我们直接获取到表中的某个字段对应的数据

    # 查询出卖出书数大于库存的书籍
    from django.db.models import F
    
    res = models.Book.objects.filter(maichu__gt=F('kucun'))
    
  2. 获取一个整型字段的数据进行运算

    # 将所有书籍的价格提升500元
    from django.db.models import F
    
    res = models.Book.objects.update(price=F('price')+500)
    
  3. 获取一个字符串字段进行拼接

    # 将所有的书籍名称后面加上爆款
    from django.db.models.functions import Concat
    from django.db.models import Value
    
    models.Book.objects.update(title=Concat(F('title'),Value('爆款')))
    # models.Book.objects.update(title=F('title') + '爆款')  # 所有的名称会全部变成空白
    

②、Q查询

  1. 修改查询条件之间的关系

    # filter括号内多个参数是and关系
    
    # 查询卖出数大于100或者价格小于600的书籍
    from django.db.models import Q
    
    res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lg=600)) # ,分割表示and关系
    res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lg=600)) # |分割表示or关系
    res = models.Book.objects.filter(~Q(maichu__gt=100),Q(price__lg=600))#~分割表示'非'关系
    
  2. Q查询的高阶用法

    # 能够将查询条件的左边也变成字符串的形式
    from django.db.models import Q
    
    q = Q()
    q.connector = 'or' # 修改关系
    q.children.append(('maichu__gt',100))
    q.children.append(('price__lt',600))
    res = models.Book.objects.filter(q) # 默认还是and关系
    

11、Django中开启事务

"""
MySQL中的事务
	ACID
		原子性
			不可分割醉笑单位
		一致性
			与原子性相辅相成
		隔离性
			事物之间互不干扰
		持久性
			事务一旦确认永久生效
    事务回滚
    	rollback
 	事务确认
 		commit
"""
"""Django中开启事务"""
from django.db immport transaction
try:
    with transaction.atomic():
        sql1
        sql2
        sql3
        ....
        # 在with代码块内写的所有orm操作都是属于同一事务
except Exception as e:
    print(e)

12、orm中常用的字段及参数

①、基本字段

"""
参数详解
	verbose_name		字段注释
	default				默认值
	null				是否允许字段为空
"""

# 主键字段
AutoField(pimary_key=True)

# 字符字段 varchar(最大限度255)
CharField(max_length=32)

# 整型字段	int
IntegerField()

# 长整型字段
BigIntegerField()

# 浮点型字段
DecimalField(max_digts=8(最大长度,包括小数),decimal_places=2(小数部分))

# 邮箱 varchar(254)
EmailFiled

# 日期类型 date  
DateField

# 精确日期类型 datetime
DateTimeField

# 日期类型的参数:
# auto_now:每次修改数据的时候都会自动更新当前时间
# auto_now_add:只在创建数据的时候记录创建时间,后续不会自动修改

# 布尔值
BooleanField  # 数据里面存0/1

# 文本类型
TextField # 可以存放大量文本内容,没有字数限制

# 文件类型   -字符类型
FileField(upload_to='文件保存的路径')
# 会给该字段穿一个文件对象,把文件本身自动保存到路径下,这个字段内保存的是文件路径

# 更多字段
直接参考博客:https://www.cnblogs.com/Dominic-Ji/p/9203990.html

②、自定义字段

# django除了给你提供了很多字段类型之外 还支持你自定义字段
class MyCharField(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        # 这里添加我们对其设置的默认关系
        self.max_length = max_length
        # 调用父类的init方法
        super().__init__(max_length=max_length,*args,**kwargs)  # 一定要是关键字的形式传入

    def db_type(self, connection):
        """
        返回真正的数据类型及各种约束条件
        :param connection:
        :return:
        """
        return 'char(%s)'%self.max_length

# 自定义字段使用
myfield = MyCharField(max_length=16,null=True)

③、外键字段及参数

# 当外键字段唯一的时候就成了一对一关系
ForeignKey(unique=True)   ===	OneToOneField()

# db_index = True 代表这个字段设置为索引

# to_field 设置要关联的表的字段,默认不写就是关联另外一张的主键字段

# on_delete 当删除关联表的数据时,对当前表关联的行为

# django2.X以上需要指定级联更新和级联删除

13、查询优化

  1. 引子

    """
    如果你仅仅只是书写了orm语句,在后面根本没有用到该语句所查询出来的结果
    那么orm会自动识别出来,直接不执行
    
    惰性查询:res = models.Book.objects.all()
    执行:print(res) 此时orm才会真正地执行
    """
    
  2. only和defer

    res = models.Publish.objects.all()
    print(res) # quertset对象
    for item in res:
        print(item.name) # 此时没循环一次就执行一次数据库命令,效率太低
        
    # 1.only
    res = models.Publish.objects.only('name')
    print(res) # queryset对象
    for item in res:
        print(item.name) # 打印only括号里的字段时,不会走数据库,效率提高
        print(item.addr) # 打印only括号里没有的字段时,每循环一次就走一次数据库,效率太低
       
    # 2.defer
    res = models.Publish.objects.defer('name') 
    print(res) # queryset对象
    for item in res:
        print(item.addr) # 打印defer括号里没有的字段时,不走数据库,效率提高
        print(item.name) # 打印defer括号里有的字段时,每循环一次就走一次数据库,效率太低
        
      
    """
    总结:
    	使用:直接在objects后面连用
    	返回:only和defer都是返回queryset对象
    	区别:
    		only:括号内指定的字段,在被查询的时候不会走数据库
    		defer:括号内没指定的字段,在被查询的时候会走数据库
    	特殊:如果仅仅使用all,后期没有使用到他的结果时,orm是不会执行的,但是一旦执行过一次,第二次拿到all的返回值是不需要重新走数据库
    """
    
  3. # 1.select_related
    res = models.Author.objectd.select_related('author_detail')
    print(res) # queryset对象
    for item in res:
        print(item.author_detail.phone) # 连表操作,连起来的表可以拿到两张子表的任意字段
        
    # 2.prefetch_related
    res = models.Author.objects.prefetch_related('author_detail')
    print(res) # queryset对象
    for item in res:
        print(item.author_detail.phone) # 子查询
       
    
    """
    总结:
    	使用: 直接在objects后面连用
        返回: only 和 defer都返回QuerySet对象
        区别: 
            select_related:   内部使用连表查询. 
                !!注意:!! 括号内只能放外键字段. 且只支持一对一, 一对多的表关系. 多对多不支持.
                内部通过1次性将2表查询出来封装成对象中, 下次查询这2表就无序走数据库了.
            prefetch_related: 内部使用子查询.  
                内部通过2次性将子查询结果查询出封装成对象中.下次查询这2表就无序走数据库了. (感觉视角: 感觉是一次性搞定的) 
    """
    

14、choices参数

  1. 引子:针对一些我们完全可以列举完全的字段记录该如何设计字段类型?例如 性别、学历、工作经验、是否婚配、是否生子等等

  2. 具体使用

    # 生成表 models.py
    class User(models.Model):
        name = models.CharField(max_length=32)
        sex_choice = (
        		(1,'男'),
            	(2,'女')
        	)
        sex = models.IntegerField(choices=sex_choices)
        
    # 创建值
    models.User.object.creater(name='jason',sex=1)
    models.User.object.creater(name='egon',sex=2)
    models.User.object.creater(name='tank',sex=3) # sex_choice中不存在着对应关系
    # 存值的时候,没有列举出来的值也能存,不报错
    
    # 获取值
    user_obj1 = models.User.objects.filter(pk=1)
    user_obj2 = models.User.objects.filter(pk=3)
    
    print(user_obj1.get_sex_display())
    print(user_obj2.get_sex_display()) # 取值的时候,没有对应关系时,该字段的信息是什么就展示什么
    

15、MTV与MVC模型

  • MTV模型
    • M:models
    • T:templates
    • V:views
  • MVC模型
    • M:models
    • T:templates
    • C:controller(控制器)
  • Django自称是MTV模型,但本质还是MVC模型

16、对对多关系的创建

①、全自动

  • 利用orm自动帮我们创建第三张表

    class Book(models.Model):
       	name = models.ChaeField(max_length=32)
    	authors = models.ManyToManyField(to='Author')
        
    class Author(models.Model):
        name = models.CharField(max_length=32)
    
  • 优点:创建第三张关系表的代码不需要自己写,还支持orm提供的第三张表的操作 add、set、remove、clear

  • 缺点:第三张表的扩展性差

②、纯手动

  • 自己写第三张关系表

    class Book(models.Model):
       	name = models.ChaeField(max_length=32)
        
    class Author(models.Model):
        name = models.CharField(max_length=32)
        
    class Book2Author(models.Model):
        book_id = models.ForeignKey(to='Book')
        author_id = models.ForeignKey(to='Author')
    
  • 优点:第三张表的扩展性好

  • 缺点:要写的代码多,不能使用orm提供的简单方法

③、半自动

  • 自己写表的同时,主动关联和第三张表的关系

    class Book(models.Model):
       	name = models.CharField(max_length=32)
        authors = models.ManyToManyField(to='Author',
                                        through='Book2Author',
                                        through_field='book','author')
    # 这个外键字段非常见名知意,to=外键关联的表,through=通过哪个表把两者相连,through_fields=(‘第三张表字段1’,‘第三张表字段2’) 这个顺序不能乱
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
        
    class Book2Author(models.Model):
        book = models.ForeignKey(to='Book')
        author = models.ForeignKey(to='Author')
    
  • 优点:扩展性好,切能使用orm的正反向查询

  • 缺点:但是不能使用add,set,remove,clear方法

posted @ 2021-01-26 15:35  今天捡到一百块钱  阅读(77)  评论(0编辑  收藏  举报