五、Django之模型层
Django之模型层
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()
-
-
单表具体操作
# 增加数据 # 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语句方法
-
方式一:在queryset对象后面.query
-
方式二:在配置文件中配置以下代码
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、正反查询概念
-
正向:外键字段在我这里,我查你,就是正向
-
反向:外键字符在你这里,我查你,就是反向
-
例子:book和publish,外键字段在book表中
- 由book查publish就是正向
- 由publish查book就是反向
-
正向查询按字段,获取多个.all()
book_obj = models.Book.objects.filter(pk=2).first() author_obj = book_obj.authors.all() # 跳转到作者表拿全部 print(author_obj)
-
反向查询按小写表名_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查询
-
F能够帮助我们直接获取到表中的某个字段对应的数据
# 查询出卖出书数大于库存的书籍 from django.db.models import F res = models.Book.objects.filter(maichu__gt=F('kucun'))
-
获取一个整型字段的数据进行运算
# 将所有书籍的价格提升500元 from django.db.models import F res = models.Book.objects.update(price=F('price')+500)
-
获取一个字符串字段进行拼接
# 将所有的书籍名称后面加上爆款 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查询
-
修改查询条件之间的关系
# 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))#~分割表示'非'关系
-
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、查询优化
-
引子
""" 如果你仅仅只是书写了orm语句,在后面根本没有用到该语句所查询出来的结果 那么orm会自动识别出来,直接不执行 惰性查询:res = models.Book.objects.all() 执行:print(res) 此时orm才会真正地执行 """
-
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的返回值是不需要重新走数据库 """
-
select_related 和 prefetch_related
# 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参数
-
引子:针对一些我们完全可以列举完全的字段记录该如何设计字段类型?例如 性别、学历、工作经验、是否婚配、是否生子等等
-
具体使用
# 生成表 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方法