django模型层
django模型层
前期准备
我们的django项目运行后 会自动创建一个sqlite3的数据库,但是sqlite3数据库对时间字段不准确,有时候会出错。
所以我们一般习惯切换使用MySQL django orm,这些数据库并不会自动生成,所以我们要先准备好。
测试django某个功能层环境搭建
我们的django默认不能像我们之前编写py文件写好了之后可以运行测试,django一旦运行整个项目就全部运行起来了。
如果想要测试某个py文件要做一下操作
测试环境1
pycharm提供的python console
在pycharm界面底部
它有一个缺点,就是我们写过的代码无法保存。
测试环境2
在我们应用目录下创建一个新的py文件或者用自带的tests文件
先去manage.py 拷贝前四行代码
然后放在我们自己创建的py文件内或tests.py文件内
接着输入两行代码:
import django
django.setup()
接着导入models 就可以测试了
django orm底层还是SQL语句 我们是可以查看的
如果我们手上是一个QuerySet对象 那么可以直接点query查看SQL语句
print(res.query) res 接收语句对象的
如果想查看所有orm底层的SQL语句也可以在配置文件添加日志记录
添加到settings文件内
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
ORM常用关键字
# 1.create() 创建数据并返回用户对象
res = models.user.objects.create(name='liaji',age=18)
print(res) # 用户对象:liaji
# 2.filter() 根据条件筛选数据 结果是QuerySet 列表套对象[对象1,对象2]
models.user.objects.filter() # 默认拿全部
models.user.objects.filter(name='tank') # 单个条件筛选
models.user.objects.filter(name='tank',age=18) # 多个条件筛选
# 3.first() last() 针对 获取到的列表套对象 拿到其中第一个或最后一个(也可以用索引但是不建议)
res = models.user.objects.filter().first() # 拿到查询所有数据后的列表套对象里的第一个
res = models.user.objects.filter().last() # 拿到查询所有数据后的列表套对象里的最后一个
res = models.user.objects.filter()[0] # 也可以根据索引位拿到数据,但是如果超出会报错,报错不好。但是使用first,last filter查询超出范围的数据并不会报错
res = models.user.objects.filter(pk=100).first() # 返回None
# 4 update() 更新数据/批量更新 不输入默认更新全部
res = models.user.objects.filter().update() # 批量更新
res = models.user.objects.filter(id=3).update(name='nxm') # 根据id查询到数据后更新名字
# 5.delete() 单独删除/批量删除
res = models.user.objects.filter().delete() # 删除全部
res = models.user.objects.filter(id=4).delete() # 根据条件删除对应数据
# 6 all() 查询所有数据
res = models.user.objects.all() # 结果也是QuerySet 列表套对象
# 7 values() 根据指定字段回去字段数据
res = models.user.objects.filter().values() # 结果也是 QuerySet 列表套字典
res = models.user.objects.all().values('name') # 拿到所有的名字
# 8 values_list() 根据指定字段获取数据 结果也是QuerySet 列表套元组
res = models.user.objects.all().values_list('name')
print(res)
# 9 distinct() 去重 根据多条数据全部一模一样的才能去重,主键就不能去重,可以按照别的字段筛选了在去重
res = models.user.objects.values('name').distinct()
# 10 order_by() 根据指定条件排序
res = models.user.objects.all().order_by('age') # 默认升序
res = models.user.objects.all().order_by('-age') # 加 - 号降序
res = models.user.objects.all().order_by('id','age') # 可以写多个,第一个排不出来按第二个排
# 11.get() 根据条件筛选数据并直接获取数据对象,取不到直接报错
res= models.user.objects.get(id=1)
# 12. exclude() 取反
res = models.user.objects.exclude(pk = 2,)
# 13.reverse() 颠倒顺序(被操作的对象必须是已经排过序的才可以)
res = models.user.objects.all()
res = models.user.objects.all().order_by('age')
res = models.user.objects.all().order_by('age').reverse()
# 14.count() 统计结果集中数据的个数
res = models.user.objects.all().count()
15.exists() 判断结果集中是否含有数据 如果有则返回True 没有则返回False
res = models.user.objects.all().exists()
res = models.user.objects.filter(pk=100).exists()
注意事项:
时间字段注意事项:
auto_now_add # 首次创建数据获取当时时间
auto_now # 每次操作数据都会自动更新时间
为了方便我们查询对象名,在模型层ORM内部使用双下str魔法方法获取对象名
def __str__(self):
return f'用户对象:{self.name}'
ORM执行SQL语句
有时候ORM的操作效率可能偏低,我们可以自己编写sql代码的.
方式1
res = models.User_info.objects.raw('select * from app01_user_info;')
注意结果是个数据集。可以使用List(res)展示出来,或者用for循环取出来
方式2
导入connection模块,内部封装了pymysql
from django.db import connection
cursor = connection.cursor()
cursor.execute('select * from app01_user_info;')
cursor.fetchall()
双下划线查询
queryset 对象可以无限制的点queryset对象的方法( fileter().values().filter() )一直点下去
# 查询年龄大于18的用户数据
res = models.User.objects.filter(age__gt=19)
"ORM里不支持 大于小于号 使用gt代表大于 lt代表小于"
# 查询年龄小于38的用户数据
res = models.User.objects.filter(age__lt=18)
__gte=18 # 大于等于
models.User.objects.filter(age__gte=18)
__lte=18 # 小于等于
models.User.objects.filter(age__lte=18)
# 查询年龄是18或者28或者38的数据
age__in
"支持成员运算符"
models.User.objects.filter(age__in(18,28,38))
# 查询年龄在18到38范围之内的数据
age__range
"查询2个范围之内的数据需要使用元组或列表包起来(范围1,范围2)"
models.User.objects.filter(age__range=(18,38))
# 查询名字中含有字母j的数据
模糊查询 name__contains
models.User.objects.filter(name__contains='j')
"注意: 区分大小写的"
name__icontains # 不区分大小写
models.User.objects.filter(name__icontains='j')
# 查询注册年份是2022的数据
查询时间按照 年月日的 英文单词(月日需要在配置文件内转时区)
models.User.objects.filter(register_time__year=2022)
关键字 | 功能 |
---|---|
gt | 大于 |
lt | 小于 |
gte | 大于等于 |
lte | 小于等于 |
in | 成员运算符查询18,或28 |
range | 查询2个数据范围之间的数据 |
contains | 模糊查询区分大小写 |
icontains | 模糊查询不区分大小写 |
ORM外键字段的创建
首先需考虑Mysql的外键关系是怎么判断的
外键关系分为3种 一对多(外键建在多的一方)----多对多(统一建在第三张关系表内)----一对一(建在查询频率高的一方)
先要判断表与表中间的关系 采用换位思考原则
第一步
创建基础表(书籍表,出版社表,作者表,作者详情表)
第二步
确定表之间的外键关系
1.外键字段可以直接建在查询频率较高的表内,多对多的外键字段ORM会自动帮你创建第三张关系表
2.自己创建第三张关系表并创建外键关系
注意事项
针对一对多或一对一 的外键同步到表中之后自动加_id 的后缀
针对多对多不会再表中有展示,而是创建第三张表
字段功能 | 字段 |
---|---|
小数字段 | DecimalField(max_length=8,decimal_places='2') 总共8位数小数占两位 |
长数字字段名 | BigIntegerField() |
外键字段 | ForeignKey() |
创建外键的方法
创建外键的方法
# 一对多
models.ForeignKey(to='表名',on_delete=models.CASCADE) # 第二个属性是级联更新级联删除
"django1 默认外键字段都是级联更新删除,django2以上版本需要自己申明"
# 多对多
models.ManyToManyField(to='表名') # 不用申明级联更新,因为是单独的关系表
# 一对一
models.OneToOneField(to='表名',on_delete=models.CASCADE)
开始建表
from django.db import models
# Create your models here.
class Book(models.Model):
"书籍表"
title = models.CharField(max_length=32, verbose_name='书名')
price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
publish_time = models.DateField(auto_now_add=True) # 出版日期
# 创建出版社外键字段(一对多)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
# 创建书和作者的关系(多对多)ORM会自动创建第三张关系表,第三张表不用申明级联更新
authors = models.ManyToManyField(to='Author')
def __str__(self):
return f'书籍对象:{self.title}'
class Publish(models.Model):
"出版社表"
name = models.CharField(max_length=32, verbose_name='出版社名')
address = models.CharField(max_length=32, verbose_name='地址')
def __str__(self):
return f'出版社对象:{self.name}'
class Author(models.Model):
"作者表"
name = models.CharField(max_length=32, verbose_name='作者名')
age = models.IntegerField(verbose_name='年龄')
# 创建作者与作者详情的一对一外键字段
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
def __str__(self):
return f'作者对象:{self.name}'
class AuthorDetail(models.Model):
'作者详情表'
phone = models.BigIntegerField(verbose_name='手机号')
address = models.CharField(max_length=64, verbose_name='家庭住址')
def __str__(self):
return f'作者详情对象:{self.phone}'
关系判断:
一对多:
一个出版社可以有多本书,一对多,外键建在查询较多的一方(Book)
多对多:
1. 一个作者可以写多本书,一本书也可以有多个作者。 把外键创在任意一方都可,
2.ORM会把他变成虚拟字段并创建一个第三方关系表
一对一:
作者与作者详情表 一对一 外键建在查询较多的一方(Author)
执行迁移命令。
外键字段操作方法
外键字段相关操作
一对多
# 一对多 插入数据可以填写表中的实际字段名
models.Book.objects.create(title='极品公子',price=48.8,publish_id=1)
models.Book.objects.create(title='神墓',price=48.8,publish_id=2)
# 一对多 插入数据也可以填写表外键字段对应的表对象(注意并不是publish_id因为publish_id是表中的实际字段名)
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='遮天',price=58.88,publish=publish_obj) # 可以帮你自动拿到对象的主键值
"一对一与一对多一致"
多对多
多对多的关系表因不是我们自己创建的所以使用models无法点出该表名
解决方法:
获取到含有对多对字段的数据对象来点其 多对多的外键名(虚拟字段)就可以直接访问到第三张表
book_obj = models.Book.objects.filter(pk=1).first() # 获取一个id值为1的书籍对象
book_obj.authors.add(1) # 给这个书籍对象添加id值为1的作者
book_obj.authors.add(2, 5) # 给这个书籍对象添加多个作者
"也可先获取作者对象然后添加"
book_obj = models.Book.objects.filter(pk=2).first() # 获取一个id值为2的书籍对象
author_obj = models.Author.objects.filter(pk=1).first()# 获取一个id值为1的作者对象
author_obj1 = models.Author.objects.filter(pk=2).first()# 获取一个id值为2的作者对象
book_obj.authors.add(author_obj,author_obj1) # 给第三张关系表id值为2的书籍添加多个作者(id1作者,id2作者)
"修改关系 set"# 先删除在新增
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.set((1,2))
# 用元组套起来,不然会报错,修改id值为1的书籍的作者为 id1 作者与id2作者,如果表内有多余的关系自动删除
book_obj.authors.set((1,))"注意,一个数据需要加逗号。[1,]列表也可以"
# 把书籍对象的作者修改成id值为1的作者。
"也可以获取作者对象 来进行修改 (作者对象,)"
'删除关系 remove'
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.remove(2,) # 删除与id2作者的关系,也可以删除多个(1,2)
"也可以获取作者对象进行删除"
"删除所有关系 clear"
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.clear() # 删除这个书籍对象的所有关系
ORM的跨表查询
正反向查询的概念(重要)
正向查询
- 由外键字段所在的表数据查询关联的表数据
反向查询
- 没有关键字段的表数据查询关联的表数据
如何判断正反向查询:
- 核心就看外键字段在不在当前数据所在的表。
- 外键字段在谁手上 基于他查关联的表数据就是正向,从关联的数据表查询含有外键字段的表的数据 就是反向
ORM跨表查询口诀
"""
正向查询按外键字段查
反向查询按表名小写查
"""
基于对象的跨表查询
正向查询
1.查询主键为1的书籍对应的出版社名称
book_obj =models.Book.objects.filter(id=1).first() # 先获取手上现有的条件主键为1的书籍对象
print(book_obj.publish.name) # 直接使用对象点外键字段就可以拿到出版社对象在通过.name 方式拿到出版社名
2.查询主键值为5的数据对应的作者姓名
book_obj = models.Book.objects.filter(pk=5).first()
print(book_obj.authors) # app01.Author.None 由于书和作者是多对多关系。可能会有很多个作者
print(book_obj.authors.all()) #< QuerySet [<Author: 作者对象:辰东>]>
# 3.查询作者辰东的电话号码
author_obj = models.Author.objects.filter(name='辰东').first()
print(author_obj.author_detail.phone)
反向查询
关键语法"小写表名_set"
先只写表名观看是否报错 如果报错则使用_set
# 4.查询北方出版社出版过的书籍名称
publish_obj = models.Publish.objects.filter(name='北方出版社').first()
print(publish_obj.book_set.all())
# 5.查询辰东写过的书籍
author_obj = models.Author.objects.filter(name='辰东').first()
print(author_obj.book_set.all()) #<QuerySet [<Book: 书籍对象:神墓>, <Book: 书籍对象:遮天>, <Book: 书籍对象:剑来>]>
# 6.查询电话号码是110的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author.name)
总结
当查询的结果可能返回多个对象使用all()获取,如何判断。点表名或字段名返回的是None
基于双下划线的跨表查询
正向查询
# 1.查询主键为1的书籍对应的出版社名称
res=models.Book.objects.filter(pk=1).values('publish__name') # 外键字段__内部字段名
print(res)
# 2.查询主键为4的书籍对应的作者姓名
res = models.Book.objects.filter(pk=4).values('authors__name')
res= models.Book.objects.filter(pk=4).values('title','authors__name')
# 也可以同时拿到书名与作者名
print(res)
# 3.查询辰东的电话号码
res = models.Author.objects.filter(name='辰东').values('author_detail__phone')
print(res)
反向查询
# 4.查询北方出版社出版过的书籍
res = models.Publish.objects.filter(name='北方出版社').values('book__title')
print(res)
# 5.查询辰东写过的书籍
res = models.Author.objects.filter(name='辰东').values('book__title')
print(res)
# 6.查询电话号码是110的作者姓名
res = models.AuthorDetail.objects.filter(phone=110).values('author__name')
print(res)
进阶操作
不准使用条件一进行查询。从后面往前面查询
# 1.查询主键为1的书籍对应的出版社名称
res = models.Publish.objects.filter(book__id=1).values('name')
print(res)
# 2.查询主键为4的书籍对应的作者姓名
res = models.Author.objects.filter(book__id=4).values('name')
print(res)
# 3.查询辰东的电话号码
res = models.AuthorDetail.objects.filter(author__name='辰东').values('phone')
print(res)
# 4.查询北方出版社出版过的书籍
res = models.Book.objects.filter(publish__name='北方出版社').values('title')
print(res)
# 5.查询辰东写过的书籍
res = models.Book.objects.filter(authors__name='辰东').values('title')
print(res)
# 6.查询电话号码是110的作者姓名
res = models.Author.objects.filter(author_detail__phone='110').values('name')
print(res)
补充
# 查询主键为4的书籍对应的作者的电话号码
res = models.Book.objects.filter(pk=4).values('authors__author_detail__phone') # 先拿到book对应的id4,然后基于作者外键字段__作者表里的详情外键字段__需要获取的值的字段名
print(res)
res= models.AuthorDetail.objects.filter(author__book__pk=4).values('phone')
# 基于电话表拿,筛选作者表里作者写的书id是4的 因为values里不能用=所以放在filter内
print(res)
res = models.Author.objects.filter(book__pk=4).values('author_detail__phone')
print(res)
聚合查询
聚合查询就是利用一些聚合函数来进行查询
常用聚合函数
关键字aggregate
函数名 | 功能 |
---|---|
avg | 平均值 |
max | 最大值 |
min | 最小值 |
sum | 求和 |
count | 计数 |
ORM中聚合函数可以单独使用 需要使用关键字来配合
只要跟模型层相关的模块都藏在
django.db 或者django.db.models里
"aggregate"
需要导入模块使用
from django.db.models import Max,Min,Sum,Count,Avg
res = models.Book.objects.aggregate(Max('price')) # 单独使用需要用字符串的形式
res = models.Book.objects.aggregate(max_price= Max('price')) # 可以起别名
res = models.Book.objects.aggregate(max_price= Max('price'),all_Count=Count('pk'),min_price=Min('price'),sum_price=Sum('price')) #多个一起用,需要注意如果多个都起别名了,那么没有别名的应该放在前面
分组查询
关键字 annotate
查询的时候可能或报错原因是因为mysql的严格模式开启了,需要取消严格模式 set global sql_mode='strict_trans_tables';
在当前服务端有效
1.统计每一本数的作者个数
res = models.Book.objects.annotate(shuliang=Count('authors__pk')).values('title','shuliang')
print(res)
# 可以给分组起别名的方式同时拿到书名与对应的作者的数量
2 统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(jiage=Min('book__price')).values('name','jiage')
print(res)
3.统计不止一个作者的书
思路: 先拿到每本书有几个作者。然后在进行条件筛选,filter放在前面相当于where 放在后面相当于having
res = models.Book.objects.annotate(shuliang=Count('authors__pk')).filter(shuliang__gt=1).values('title','shuliang')
print(res)
4查询出每个作者的出售的书籍总和
res = models.Author.objects.annotate(zonghe=Sum('book__price')).values('name','zonghe')
print(res)
注意:
models.表名.objects.annotate() 按照表分组
models.表名.oobjects.values(‘字段名’).annotate() 按照values括号内字段分组
# 查看每个出版社的书的总和
res = models.Book.objects.values('publish_id').annotate(zonghe=Count('pk')).values('publish_id','zonghe')
print(res)
F与Q查询
注意在模型层给表添加字段后执行迁移会提示需要默认值,需要在新增的字段后增加默认值
default=100 字段默认100数量
null=True 允许字段为空
查询数据不是明确的,但也需要从数据库中获取 需要使用F查询
F查询
from django.db.models import F
1.查询库存大于卖出数的书籍
res = models.Book.objects.filter(kucun__gt=F('shouchu'))
print(res)
2.将所有的书籍价格涨66
res = models.Book.objects.update(price=F('price')+66)
print(res)
将ID值为1的书籍名字后面追加爆款
与整型不同如果需要+名字的话那么需要使用模块
from django.db.models.functions import Concat # 拼接
from django.db.models import Value #新的值
res = models.Book.objects.filter(pk=1).update(title=Concat(F('title'),Value('爆款')))
print(res)
Q查询
如果需要把filter括号内的and关系改成or 那么就需要使用Q查询
from django.db.models import Q
1.查询主键是1或者价格大于1000的书籍
res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=100))
print(res)
用Q把条件包起来
Q关系功能符 | 功能说明 |
---|---|
逗号隔开 | and关系 |
‘|’ 隔开 | or关系 |
~ | not关系 |
Q查询进阶操作
我们在Q查询时括号内输入的是字段名=值,但是Q查询也可以在括号内填写字符串。
# 其实Q本身是个类,Q()就可以产生一个Q对象
from django.db.models import Q
q_obj = Q() # 产生q对象
# 在Q查询本身中我们使用 | , ~ 来修改关系,这里提供了另一个方法
q_obj.connector = 'or' # 默认是and 可以修改
q_obj.children.append(('pk',2)) # 添加查询条件
q_obj.children.append(('price__gt',100)) # 支持多个条件
res = models.Book.objects.filter(q_obj) # 可以直接把Q对象放进去
print(res)
ORM查询优化
ORM查询特性
1.ORM的查询默认都是惰性查询 # 如果不执行打印操作django就不会执行
2.ORM的查询自带分页处理 # django帮助我们给每次查询语句都添加了LIMIT 限制分页展示 防止数量巨大的表
3.only与defer
# 获取数据对象 指定字段的数据
res = models.Book.objects.only('price','title')
for i in res:
print(i.price)
print(i.title)
"帮助我们封装了指定字段的数据到对象内,当点括号内字段时不会走SQL查询,直接从数据对象内部查询,也可以点括号内没有的字段进行查询查一次走一次SQL查询。"
res = models.Book.objects.defer('price','title')
for i in res:
print(i.price)
print(i.title)
"与only相反 点括号内填写的字段会走sql查询,点括号内不存在的字段不会走sql查询"
第二组
select_related(连表) 与 prefetch_related(子查询)
"只能支持外键 并只能填写一对多或者一对一的关系"
正常情况下如果不使用select_related 用对象去点一个外键关系表内的数据 点一次就会执行一次sql语句。
res = models.Book.objects.select_related('Publish')
# 先把两个表的做连表 后封装到数据对象内
for i in res:
print(i.Publish.name) # 只走一条sql
res = models.Book.objects.prefetch_related('Publish')
for i in res:
print(i.publish.name) # 走了两条查询
# 区别在prefetch底层是子查询,先帮助我们查到书籍表再查到外键关系表 然后封装起来
ORM事物操作
在ORM中进行事物操作需要在settings内与sql连接参数里加上一条参数
# 只要视图函数内部有报错 就会自动回滚
1.ATOMIC_REQUESTS:True,
" 全面开启事物,每次请求涉及到的ORM操作的视图函数处于一个事物"
2. 装饰器方法
from django.db import transaction
@transaction.atomic
def index(request):
ORM操作
return HttpResponse('返回值')
"针对被装饰的视图函数内部的事物"
3.with上下文管理
def func(request):
print('12')
with transaction.atomic():
ORM操作
return HttpResponse('返回值')
"with上下文管理中的事物"
"注意:虽说往前端 返回值的方法是三板斧,但是这里return 123 也不会报错。不会触发事物"
ORM常用字段类型
1.AutoField # 自增字段
常用属性: primary_key=True
2.CharField # 对应的是sql的varchar
常用属性: max_length=32 verbose_name='字段注释'
3.IntegreField # 整型字段
4.BigIntegreField # 长数字整型字段
5.DecimalField # 小数字段
常用属性: max_digits=数字总长度, decimal_places=小数占几位
6.DateField # 时间字段 年月日
常用属性: auto_now auto_now_add
7.DateTimeField # 时间字段 年月日时分秒
常用属性: auto_now auto_now_add
8.BooleanField # 布尔值类型
传布尔值自动存0或1
9.TextField # 存储大文本
10.EmailField # 邮箱格式
11.FileField # 文件字段
传文件对象,自动保存到提前配置好的路径下 并存储文件路径信息。
upload_to = "" # 上传文件的保存路径
storage= None # 存储组件
ORM支持自定义字段
ORM还支持用户自定义字段类型
class MyCharField(models.Field):
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):
return 'char(%s)' % self.max_length
class User(models.Model):
name = models.CharField(max_length=32)
info = MyCharField(max_length=64)
ORM常用字段参数
primary_key 主键
verbose_name 注释
max_length 字段长度
max_digits 小数总共多少位
decimal_places 小数点后面的位数
auto_now 每次操作数据自动更新时间
auto_now_add 首次创建自动更新时间后续不自动更新
null 允许字段为空
default 字段默认值
unique 唯一值
db_index 给字段添加索引
choices 当某个字段的可能性能够被列举完全的情况下使用
性别、学历、工作状态、...
class User(models.Model):
name = models.CharField(max_length=32)
info = MyCharField(max_length=64)
# 提前列举好对应关系
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice,null=True)
"ORM操作"
user_obj = User.objects.filter(pk=1).first()
user_obj.gender # 只能取到对应关系的一个参数
user_obj.get_gender_display() # 可以获取数字对应的性别。
to 关联表
to_field 关联字段(不写默认关联数据主键)
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。
on.delete参数的配置(了解)
1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
多对多的三种创建方式
1.全自动创建
"""""
优势: 自动创建第三张表,并且提供了 add remove set clear 四种操作方法
劣势: 无法添加更多的字段,扩展性差
"""
class book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
# 创建多对多关系
authors = ManyToManyField(to='author')
class author(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=64)
2.纯手动创建
"""
优势:第三张表自己创建 扩展性强
劣势: 编写繁琐,不在支持 add set remove clear 四种操作方法及正反向概念
"""
# 书籍表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
# 作者表
class Author(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=64)
# 手撸第三张关系表并扩展
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author= models.ForeignKey(to='Author')
others = models.CharField(max_length=32)
join_time = models.DateTimeField(auto_now_add=True)
3.半自动创建
# 书籍表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
#手动设置 指定多对多关系表 并指定不要自动创建第三张关系表
authors = models.ManyToManyField(to='Author',
through ='Book2Author',
through_fields=('book','author'))
"注意,在那张表里创建关系就把哪张表放前面"
# 作者表
class Author(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=64)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book',on_delete=models.CASCADE)
author= models.ForeignKey(to='Author',on_delete=models.CASCADE)
others = models.CharField(max_length=32)
join_time = models.DateTimeField(auto_now_add=True)
"""
优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用
劣势:编写繁琐不再支持add、remove、set、clear"""