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)
    执行迁移命令。

image

外键字段操作方法

外键字段相关操作
一对多
# 一对多 插入数据可以填写表中的实际字段名
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"""
posted @ 2022-12-14 21:17  李阿鸡  阅读(58)  评论(0编辑  收藏  举报
Title