django之模型层
模型层
一.模型层前期准备
1.django自带一个sqlite3小型数据库
该数据库对时间字段不敏感,有时候会展示错乱,所以我们习惯切换成常见的数据库,比如MySQL,django orm并不会自动帮你创建库,所以要提前准备好
2.django切换MySQL数据库
"""DATABASES配置:""" DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 指定数据库软件名称 'NAME': 'django01', # 指定库名 'USER': 'root', 'PASSWORD': '123456', 'HOST': '127.0.0.1', 'PORT': 3306, 'CHARSET': 'utf8' } }
3.定义模型类
class User(models.Model): uid = models.AutoField(primary_key=True, verbose_name='编号') name = models.CharField(max_length=32, verbose_name='姓名') age = models.IntegerField(verbose_name='年龄') join_time = models.DateField(auto_now_add=True) """ 日期字段 DateTimeField:年月日时分秒 DateField:年月日 日期字段重要参数 auto_now:每次操作数据并保存都会自动更新当前时间 auto_now_add:只在创建数据的那一刻自动获取当前时间 之后如果不人为更改则不变 """
4.执行数据库迁移命令(模型类>>>表)
python manage.py makemigrations python manage.py migrate
5.模型层测试准备
默认是不允许单独测试某个py文件,如果想要测试某个py文件(主要models.py)
-
方式一:pycharm提供的测试环境
python console命令行测试环境
终端的形式,代码没法保存下来,关掉以后代码就没有了,不会保存下来,只是临时的
-
方式二:在任意空的py文件或者tests.py文件中
'''复制mange.py文件的前四行代码 放入一个空白的py文件中 但是不要放入已有的文件中''' import os def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djang04.settings') import django # 这个导入文件只能放在这里,放在外面会报错 django.setup() from app01 import models # 这个导入文件也只能放在这里,放在外面会报错 print(models.User.objects.filter()) if __name__ == '__main__': main()
6.ORM底层SQL语句
django orm底层还是SQL语句,我们是可以查看的
如果我们手上是一个QuerySet对象,那么可以直接点query查看SQL语句
如果想查看所有底层的SQL语句也可以在配置文件中添加日志记录
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常见查询关键字
-
create()
创建数据,返回值就是当前创建的数据对象,可以通过点的方式去获取数据对象里面具体的某个字段的值
1.当需要查询数据主键字段值的时候 可以使用pk忽略掉数据字段真正的名字 2.在模型类中可以定义一个__str__方法 便于后续数据对象被打印展示的是查看方便 3.Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数 4.虽然QuerySet支持索引但是当QuerySet没有数据的时候索引会报错 推荐使用first 1.create() 创建数据 返回值就是当前创建的数据对象 ps:还可以利用类实例化对象然后调用save方法创建 2.update() 更新数据 3.delete() 删除数据 import os def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djang04.settings') import django django.setup() from app01 import models print(models.User.objects.filter()) res = models.User.objects.create(name='kevin', age=38) print(res) user_obj = models.User(name='oscar', age=20) user_obj.save() # 对象可以通过点的方式取值 print(res.name) print(res.age) print(res.join_time) # 获取主键 print(res.uid) print(res.pk) main() """ 查找主键的时候有俩中方式: 1.我们可以通过主键名去使用点的方式去获取 2.也可以通过点pk得方式去获取 查找数据对象的主键以后我们创建表的时候可能会创建好多表,那么每个表中的主键名可能不一样,我们在要获取表对象的主键得时候,需要知道表的主键名,使用pk的话它会自动帮我们去找 """ -
filter()
筛选数据,返回值是个QuerySet对象(可以看成是列表套数据对象)
1.括号内不写查询条件,默认就是查询所有 2.括号内可以填写条件,并且支持多个,逗号隔开,默认是and关系 res = models.User.objects.fileter(name='jason', age=18) print(res) # <QuerySet [<User: 对象jason>]> -
all()
查询所有数据,返回值是一个QuerySet对象(可以看成是列表套数据对象)
res = models.user.objects.all() print(res) # <QuerySet [<User: 对象jason>, <User: 对象kevin>, <User: 对象tony>]> -
first()
获取QuerySet对象中的第一个数据对象,如果空则返回None
res = models.User.objects.all().first() print(res) # <QuerySet [<User: 对象jason>]> -
last()
获取QuerySet对象中的最后一个数据对象,如果空则返回None
res = models.User.objects.all().last() print(res) # <QuerySet [<User: 对象tony>]> -
get()
直接根据条件查询具体的数据对象,但是条件不存在直接报错,不推荐使用
直接根据条件查询具体的数据对象 但是条件不存在直接报错 不推荐使用 res = models.User.objects.get(pk=2) print(res) # 对象:kevin 这个方法与filter方法对比: res = models.User.objects.get(pk=2) print(res) # 对象:kevin res = models.User.objects.filter(pk=2).first() print(res) # 对象:kevin 虽然看起来get方法可能更简单一点,但是不推荐使用get方法 当查询查询条件不存在的时候: res = models.User.objects.get(pk=21) print(res) # 直接报错 res = models.User.objects.filter(pk=21).first() print(res) # None -
values()
指定查询字段,结果是QuerySet对象(可以看成是列表套字典数据)
res = models.User.objects.values() print(res) # [{},{}] -
value_list()
指定查询字段,结果是QuerySet对象(可以看成是列表套元组数据)
res = models.User.objects.all().values_list('name','age') # [(),(),(),()] -
order_by()
根据指定条件排序,默认是升序,字段前面加负号就是降序
res = models.User.objects.all().order_by('age') models.User.objects.all().order_by('-age') print(res) -
count()
统计orm查询之后结果集中的数据格式
res = models.User.objects.all().count() print(res) # 3 -
distinct()
针对重复的数据进行去重,一定要注意数据对象中的主键
-
exclude()
针对括号内的条件取反进行数据查询QuerySet(可以看成是列表套数据对象)
res = models.User.objects.exclude(pk=2) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:tony>]> -
reverse()
针对已经排了序的结果做颠倒
-
exists()
判断查询结果集是否有数据,返回布尔值,但是几乎不用因为所有数据自带布尔值
-
raw()
执行SQL语句,还可以借助于模块
from django.db import connection cursor = connection.cursor() cursor.execute("insert into user(name) VALUES ('lisa')") cursor.execute("update user set name='lisa' WHERE name='jennie'") cursor.execute("delete from user where name='lisa'") cursor.execute("select * from user") cursor.fetchone() cursor.fetchall()
三.ORM执行SQL语句
1.ORM编写SQL语句
有时候ORM的操作效率可能偏低,我们是可以自己编写SQL的
方式1: models.User.objects.raw('select * from app01_user;') ----------------------------------------------------- 方式2: from django.db import connection cursor.execute('select name from app01_user;') print(cursor.fetchall)
2.神奇的双下划线查询
queryset对象可以无限制的点queryset对象的方法
-
<1>.比较运算符
类型 属性 字段__gt 大于 字段__lt 小于 字段__gte 大于等于 字段__lte 小于等于 res = models.User.objects.filter(age__lte=18) print(res) # # <QuerySet [<User: 对象:jason>]> -
<2>.成员运算符
字段__in res = models.User.objects.filter(age__in=(18, 28, 38)) print(res) -
<3>.范围查询(数字)
字段名__range res = models.User.objects.filter(age__range=(18, 38)) print(res) -
<4>.模糊查询
字段名__contains 不忽略大小写 字段名__icontains 忽略大小写 res = models.User.objects.filter(name__contains='j') print(res) # <QuerySet [<User: 对象:jason>]> --------------------------------------------------- res = models.User.object.filter(name__icontains='j') print(res) # <QuerySet [<User: 对象:jason>], <User: 对象:Jerry>]> -
<5>.日期处理
字段__year 字段__month 字段__day
3.ORM外键字段的创建
-
MySQL外键关系复习
一对多 外键字段建在多的一方 多对多 外键字段统一建在第三张关系表 一对一 建在任何一方都可以,但是建议建在查询频率较高的表中 """ ps: 关系的判断可以采用换位思考原则,熟练之后可以瞬间判断 """ -
<1>.数据准备
创建基础表(书籍表、出版社表、作者表、作者详情)
-
<2>.一对多
ORM中外键字段建在多的一方 models.ForeignKey() 会自动添加_id后缀 -
<3>.多对多
ORM中有三种创建多对多字段的方式 models.ManyToManyField() 方式1:直接在查询频率较高的表中填写字段即可,自动创建第三张关系表 方式2:自己创建第三张关系表 方式3:自己创建第三张关系表,但是还是要orm多对多字段做关联 -
<4>.一对一
ORM中外键字段建在查询频率较高的表中 models.OneToOneField() 会自动添加_id后缀 publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE) author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) """ django1.X针对models.ForeignKey()/models.OneToOneField()内不需要添加on_delete=models.CASCADE django2X及以上则需要添加on_delete=models.CASCADE """
4.外键字段数据操作
-
<1>.一对多
针对一对多,插入数据可以直接填写表中的实际字段
models.Book.objects.create(title='三国演义', price=888.88, publish_id=1) models.Book.objects.create(title='人性的弱点', price=777.55, publish_id=1) 针对一对多,插入数据也可以填写表中的类中字段名
publish_obj = models.Publish.objects.filter(pk=1).first() models.Publish.objects.create(title='水浒传', price=555.66, publish=publish_obj) -
<2>.一对一
一对一与一对多外键字段的操作是一致的
既可以传数字也可以传对象
-
<3>.多对多
models.ManToManyField(to='Author') add() 添加数据,括号内即可以填写数字也可以填写数据对象,支持多个 方法一: book_obj = models.Book.object.filter(pk=3).first() book_obj.author.add(1) book_obj.author.add(2, 3) 方法二: book_obj = models.Book.objects.filter(pk=3).first() author_obj1 = models.Author.objects.filter(pk=3).first() author_obj2 = models.Author.objects.filter(pk=4).first() book_obj.author.add(author_obj1, author_obj2) --------------------------------------------------- remove() 删除数据,括号内既可以填写数字,也可以填写数据对象,支持多个 book_obj = models.Book.objects.filter(pk=3).first() author_obj1 = models.Author.objects.filter(pk=3).first() author_obj2 = models.Author.objects.filter(pk=4).first() book_obj.author.remove(1) book_obj.author.remove(2, 3) book_obj.author.remove(author_obj1) book_obj.author.remove(author_obj1, author_obj2) --------------------------------------------------- set() 修改数据,括号内必须是可迭代对象 book_obj.author.set([2, ]) book_obj.author.set([2, 3]) book_obj.author.set([author_obj1, ]) book_obj.author.set([author_obj1, author_obj2]) --------------------------------------------------- clear() 清空指定数据,括号内不需要任何参数 book_obj.author.clear()
四.ORM跨表查询
1.正反向查询概念☆
-
正向查询
由外键字段所在的表数据查询关联的表数据 正向
-
反向查询
没有外键字段的表数据查询关联的表数据 反向
-
ORM跨表查询口诀
正向查询按外键字段
反向查询按表名小写
2.基于对象的跨表查询
正向查询
-
<1>.查询主键为1的书籍对应的出版社名称
'''先根据条件获取数据对象''' book_obj = models.Book.objects.filter(pk=1).first '''再判断正反向的概念,由书查出版社外键字段在书所在的表中,所有是正向查询''' print(book_obj.publish.name) # 出版社: 北方出版社 -
<2>.查询主键为4的书籍对应的作者姓名
'''先根据条件获取数据''' book_obj = models.Book.objects.filter(pk=4).first() '''再判断正反向的概念,由书查作者外键字段在书所在的表中,所有是正向查询''' print(book_obj.authors) # app01.Author.None '''因为一本书可能是多个作者联合出版,此时直接点authors返回的就是None,因此要再点all()才能查的出来''' print(book_obj.authors.all()) '''如果指向获取作者名也可以在all后面继续点values('name')''' print(book_obj.authors.all().values('name')) -
<3>.查询jason的电话号码
'''先根据条件获取数据''' author_obj = models.Author.objects.filter(name='jason').first() '''再判断正反向的概念,由作者查作者信息外键字段在作者所在的表中,所有是正向查询''' print(author_obj.author_detail.phone)
反向查询
-
<1>.查询北方出版社出版过的书籍
'''先根据条件获取数据''' publish_obj = models.Publish.objects.filter(name='北方出版社').first() '''再判断正反向的概念,由出版社查书籍外键字段在出版社所在的表中,所有是反向查询''' print(publish_obj.book_set.all()) # 反向查询固定格式: 表名_set """ 注意:但凡所查的数据不止一个都需要在查询的后面加all(),否则返回的就是None """ -
<2>.查询jason写过的书籍
查询思路与上一题一致 author_obj = models.Author.objects.filter(name='jason').first() print(author_obj.book_set.all()) -
<3>.查询电话号码是110的作者姓名
author_obj = models.AuthorDetail.objects.filter(phone=110).first() print(author_detail_obj.author) print(author_detail_obj.author.name)
3.基于双下划线的跨表查询
正向查询
-
<1>.查询主键为1的书籍对应的出版社名称
res = models.Book.object.filter(pk=1).values('publish__name','title') print(res) -
<2>.查询主键为4的书籍对应的作者姓名
res = models.Book.object.filter(pk=4).values('author__name','title') print(res) -
<3>.查询jason的电话号码
res = models.Author.objects.filter(name='jason').values('author_detail__phone') print(res)
反向查询
-
<1>.查询北方出版社出版过的书籍名称和价格
res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price','name') print(res) -
<2>.查询jason写过的书籍名称
res = models.Author.object.filter(name='jason').vblues('book__title', 'name') print(res) -
<3>.查询电话号码110的作者姓名
res = models.Author.object.filter(phone=110).values('author__name', 'phone') print(res)
4.跨表查询的进阶操作
正向查询
-
<1>.查询主键为1的书籍对应的出版社名称
res = models.Publish.objects.filter(book__pk=1).values('name') print(res) -
<2>.查询主键为4的书籍对应的作者姓名
res = models.Author.objects.filter(book__pk=4).values('name','book__title') print(res) -
<3>.3.查询jason的电话号码
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone') print(res)
反向查询
-
<4>.查询北方出版社出版过的书籍名称和价格
res = models.Book.objects.filter(publish__name='北方出版社').values('title','price') print(res) -
<5>.查询jason写过的书籍名称
res = models.Book.objects.filter(authors__name='jason').values('title') print(res) -
<6>.查询电话号码是110的作者姓名
res = models.Author.objects.filter(author_detail__phone=110).values('name') print(res)
5.双下划线补充操作
当我们想要同时跨多张相关联的表去查询获取数据,可以使用多双下划线实现:表名__表名__字段名
查询主键为4的书籍对应的作者的电话号码 res = models.Book.objects.filter(pk=4).values('authors__author_detail__phone') print(res) res = models.AuthorDetail.objects.filter(author__book__pk=4).values('phone') print(res) res = models.Author.objects.filter(book__pk=4).values('author_detail__phone') print(res)
五.聚合查询
1.聚合函数
我们常用的聚合函数有max、min、sum、avg、count
-
<1>.使用聚合函数之前需要导入模块
from diango.db.models import Max, Min, Sum, Avg, Count -
<2>.聚合函数的使用
聚合函数通常情况下是配合分组一起使用的
-
<3>.关键字aggregate
没有分组之前如果单纯的使用聚合函数,需要关键字aggregate
查询所有书的平均价格
res = models.Book.object.aggregate(Avg('price')) print(res) # # {'price__avg': Decimal('183.117778')} res = models.Book.object.aggregate(Max('pricr'), Min('price'), Sum('price'), Avg('price'), Count('pk')) print(res)
六.分组查询
如果我们执行orm分组查询报错,并且有关键字 sql_mode strict mode
打开MySQL 输入>>>:show variables like '%mode%'; 分组有一个特性 默认只能够直接获取分组的字段 其他字段需要使用方法 我们也可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可
示例1:
统计每一本书的作者个数 res = models.Book.object.annotate(author_num=Count('authors__pk')).values('title', 'author_num') print(res) """ 1.按照整条数据分组 models.Book.objects.annotate() 按照一条条书籍记录分组 2.按照表中某个字段分组 models.Book.objects.values('title').annotate() 按照annotate之前values括号中指定的字段分组 """
1.分组查询
annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所以使用前从django.db.models引入Avg, Max, Count, Sum(首字母大写))
2.返回值
分组后,用values取值,则返回值是QuerySet书籍类型里面为一个个字典 分组后,用values_list取值,则返回值是QuerySet数据类型里面为一个个元组
MySQL中的limit相当于ORM中的QuerySet数据类型的切片
3.分组查询关键字
annotate() 里面放聚合函数 1.values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。 2.values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。 3.filter放在 annotate 前面:表示where条件 4.filter放在annotate后面:表示having
跨表分组查询本质就是将关联表join成一张表,再按单表得到思路进行分组查询
七.F与Q查询
导入模块
from django.db.models import F,Q
F查询
能够帮助你直接获取到列表中某个字段对应的数据 注意: 在操作字符串类型的数据的时候, F不能够直接做到字符串的拼接
# 1.查询库存数大于卖出数的书籍 '''当查询条件不是明确的 也需要从数据库中获取 就需要使用F查询''' from django.db.models import F res = models.Book.objects.filter(kucun__gt=F('maichu')) print(res) # 2.将所有书的价格涨800 models.Book.objects.update(price=F('price') + 800) # 3.将所有书的名称后面追加爆款 from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.update(title=Concat(F('title'), Value('新款')))
在用ORM进行查询的时候,我们会发现当查询条件不明确的时候不能执行语句,因此需要用到F查询
Q查询
# 查询主键是1或者价格大于2000的书籍 res = models.Book.objects.filter(pk=1, price__gt=2000) # 逗号默认是and关系 from django.db.models import Q res = models.Book.objects.filter(Q(pk=1), Q(price__gt=2000)) # 逗号是and res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000)) # |是or res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000)) # ~是not print(res.query)
Q查询进阶操作
可以将查询条件的字段改为字符串形式
from django.db.models import Q q_obj = Q() # 1.先产生q对象 q_obj.cennector = 'or' # 默认多个条件的连接是and可以修改成or q_obj.children.append(('pk', 1)) # 2.添加查询条件 q_obj.children.append(('price__ge', 2000)) # 支持添加多个 res = models.Book.object.filter(q_obj) # 查询支持直接填写q对象 print(res)
八.ORM查询优化
django orm默认都是惰性查询 当orm的语句在后续的代码中真正需要使用的时候才会执行 django orm自带limit分页 减轻数据库端以及服务端的压力
1.ORM查询优化之only(单表)
res =models.Book.objects.only('title', 'price') print(res) # queryset [数据对象、数据对象] for obj in res: print(obj.title) print(obj.price) # 点击括号内填写的字段不走SQL查询 print(publish_time) # 可以点击括号内没有得到字段获取数据,但是会走SQL查询比较浪费资源
点击括号内填写的字段不走SQL查询
可以点击括号内没有得到字段获取数据,但是会走SQL查询比较浪费资源
2.ORM查询优化之defer
与only刚好相反,点击括号内的会走SQL查询,但是点击括号内没有得到的字段不走SQL语句
res =models.Book.objects.defer('title', 'price') print(res) # queryset [数据对象、数据对象] for obj in res: print(obj.title) print(obj.price) # 点击括号内填写的字段,走SQL查询比较浪费资源 print(publish_time) # 点击括号内没有得到字段获取数据,不会走SQL查询
点击括号内填写的字段
点击括号内没有的字段
3.ORM查询优化之select_related(多表)
select_related括号内只能接收外键字段(一对多 一对一) 它会在内部自动拼表 得出的数据对象在点击表中数据的时候都不会再走数据库查询
res = models.Book.objects.all() for obj in res: print(obj.publish.name) # 每次查询都需要走SQL
res = models.Book.objects.select_related('publish') # 先连表后查询封装 print(res) for obj in res: print(obj.publish.name) # 不在走SQL查询
res1 = models.Authors.objects.select_related('author') # 括号内不支持多对多字段,其他两个都可以 print(res1)
4.ORM查询优化之prefetch_related(多表)
prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的
res = models.Book.object.prefetch_related('publish') # 子查询 for obj in res: print(obj.publish.name)
额外补充个知识:当表中已经有数据的情况下 添加新的字段需要指定一些参数
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) publish_time = models.DateTimeField(auto_now=True) publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) author = models.ManyToManyField(to='Author') ''' 当表中已经有数据的情况下 添加新的字段需要指定一些参数 1.设置字段值存于为空 null=True 2.设置字段默认值 default=1000 3.在终端直接给出默认值 ''' storage_num = models.IntegerField(verbose_name='库存数', null=True) sale_num = models.IntegerField(verbose_name='卖出数目', default=1000) def __str__(self): return f'对象:{self.title}'
九.ORM事务操作
django orm中至少提供里三种开启事务的方式
方式一
在settings.py配置文件数据库相关添加键值对>>>:全局有效
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'day07', 'USER': 'root', 'PASSWORD': '123', 'HOST': '127.0.0.1', 'PORT': '3306', 'CHARSET': 'utf8', "ATOMIC_REQUESTS": True, } } '''每次请求所涉及到的orm操作同属于一个事务'''
方式二
使用orm提供的装饰器>>>:局部有效
先导入装饰器所需要的模块
from django.db import transaction
然后使用装饰器开启事务
@transaction.atomic def index():pass
方式三
with上下文管理>>>:局部有效
from django.db import transaction def reg(): with transaction.atomic(): pass
十.ORM常用字段类型
AutoField(int自增列)
int自增列,必须填入参数primary_key=True。当model中如果没有自增列,则会自动创建一个列名为id的列
CharField(varchar)
verbose_name:字段的注释
max_length:字符长度
IntegerField(int)
一个整数类型,-2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)
BigIntergerField(bigint)
长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
DecimalField(decimal)
max_digits=8:总共位数
decimal_places=2:小数位
EmailField(vachar(254))
邮箱字段类型,本质还是vachar
DateField(date)
日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()
DateTimeField(datetime)
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。 auto_now:每次修改数据的时候都会自动更新当前时间 auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
BooleanField(Field)(布尔值类型)
该字段传布尔值(False/Ture)数据库里面存0/1
TextField(Field)(文本类型)
该字段可以用来存大段内容(文章)没有字数限制
FileField(Field)(字符类型)
upload_to = “/data”
给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
/data/a.txt
ForeignKey()
外键
OneToOneField
一对一关系
ManyToManyField
多对多关系
ORM还支持自定义字段
django orm除了给你提供了很多字段类型之外,还支持你自定义字段
# 定义char类型 # 字段类内部都继承Field class MyCharField(models.Field): # 字符类型都必须有max_length表示字符长度 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): # 返回真真正的数据类型及各种约束条件 return 'char(%s)' % self.max_length # 自定义字段使用 myfield = MyCharField(max_length=16,null=True) # 可以为空
十一.ORM常用参数
1.字段参数
primary_key
主键
verbose_name
字段注释
max_length
字段长度
max_digits
小数总共多少位
decimal_places
小数点后面的位数
null
允许字段为空
default
字段默认值
unique
唯一值
db_index
给字段添加索引
2.DateField与DateTimeField参数
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段
3.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) user_obj = User.objects.filter(pk=1).first() user_obj.gender user_obj.get_gender_display()
4.字段参数
to
设置关联的表
to_field
设置要关联的字段
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为
1.models.CASCADE 级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除 2.models.SET_NULL 当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空 3.models.PORTECT 当主表中的一行数据删除时,由于表中相关字段是手保护的外键,所以都不允许删除 4.models.SET_DEFAULT 当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值 5.models.SET() 当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数 6.models.DO_NOTHING 什么都不做,一切都看数据库级别的约束,注意数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
十二.多对多三种创建方式
1.自动创建
authors = models.ManyToManyField(to='Author') """ 优势:第三张表自动创建,并且提供了add、remove、set、clear四种操作 劣势:第三张表无法创建更多的字段,扩展性较差 """
2.手动创建
class Book(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey('Author') others = models.CharField(max_length=32) join_time = models.DateField(auto_now_add=True) """ 优势:第三张表完全由自己创建,扩展性强 劣势:编写繁琐,并且不在支持add、remove、set、clear以及正反向概念 """
3.半自动创建
class Book(models.Model): title = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book','author') ) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book', on_delete=models.CASCADE) other = models.CharField(max_length=32) join_time = models.DateField(auto_now_add=True) """ 优势:第三张表完全由自己创建,扩展性强,正反向概念依然清晰可用 劣势:编写繁琐不再支持add、remove、set、clear """
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix