Django模型层
目录
测试脚本
# 如果只想测试django中某一个py文件,则不用书写全部前后端交互的代码,而是直接写一个脚本即可
# 测试环境准备,在test.py中:
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django模型层.settings') #修改项目名
import django
django.setup()
# 此处写测试代码,注意缩进
单表操作
from app01 import models
# 增
# models.User.objects.create(name='jason',age=19,register_time=timezone.now()) #返回被创建的对象
# user_obj = models.User(name='egon',age=20,register_time='2020-11-11')
# user_obj.save()
# 删
# res = models.User.objects.filter(pk=2).delete() #批量删除
# del_obj = models.User.objects.filter(pk=3).first()
# del_obj.delete() #单独删除
# 改
# models.User.objects.filter(pk=4).update(name='egon') #批量改
# 查
'''
1.all() 查询所有
2.filter() 带有过滤条件查询
3.get() 查询数据对象,条件有误直接报错
4.first() 拿QuerySet第一个元素
5.last() 拿QuerySet第一个元素
6.values()
res = models.User.objects.values('name','age)
返回仅带有指定字段的QuerySet(列表套字典)
7.values_list()
res = models.User.objects.values_list('name','age)
返回仅带有指定字段的QuerySet(列表套元组)
print(res.query) QuerySet对象可以使用.query查看内部sql语句
8.distinct()
去重一定要是一模一样的数据,所以要排除主键
9.order_by()
res = models.User.objects.order_by('age')
默认升序
res = models.User.objects.order_by('-age') 降序
10.reverse()
先排序,后翻转
11.count()
res = models.User.objects.count()
12.exclude()
res = models.User.objects.exclude(name='jason)
排除符合参数条件的记录
13.exists()
res = models.User.objects.filter(name='jason).exists()
'''
查看内部sql语句的方式
# 一、QuerySet对象可以使用.query查看内部sql语句
res = models.User.objects.values_list('name','age)
print(res.query)
# 二、配置settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
双下划线查询
res = models.User.objects.filter(age__gt=20) # 年龄大于20
res = models.User.objects.filter(age__lt=20) # 年龄小于20
res = models.User.objects.filter(age__gte=20) # 年龄大于等于20
res = models.User.objects.filter(age__in=[10,20,30]) # 年龄等于10或者20或者30
res = models.User.objects.filter(age__range=[18,40]) # 年龄大于等于18小于等于40
res = models.User.objects.filter(name__contains='n') # name字段包含'n'的,默认区分大小写
# 忽略大小写:
res = models.User.objects.filter(name__icontains='n')
# ignore
res = models.User.objects.filter(name__startwith='n') # name字段以'n'开头的
res = models.User.objects.filter(name__endwith='n')
res = models.User.objects.filter(register_time__month='12') # 注册时间为12月份的
# 好多方法自己点出来看啊
外键增删改
# 一对多外键增删改
# 增
models.Book.objects.create(name='三国',price=123.23,publish_id=1)
pub_obj = models.Publish.objects.filter(pk=3).first()
models.Book.objects.create(name='红楼', price=123.00, publish=pub_obj)
# 删
models.Publish.objects.filter(pk=3).delete() #级联删除
# 改
models.Book.objects.filter(pk=5).update(publish_id=4)
models.Book.objects.filter(pk=5).update(publish=pub_obj)
# 多对多外键增删改,即对中间表的操作
# 增
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(1,6,7)
book_obj = models.Book.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=6).first()
book_obj.authors.add(author_obj1,author_obj2)
# 删
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.remove(7) # 同样可以传对象,支持多个
# 改
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.set([1,8])# 参数需要是可迭代对象,参数内同样可以传对象,支持多个,set不会改变与参数重复的原纪录,会删去不在参数中的纪录,不回收主键值,另新建只在参数中有的纪录
# 清空,即在中间表中清空某本书籍与作者的绑定关系
book_obj = models.Book.objects.filter(pk=5).first()
book_obj.authors.clear() # 无需传参
正反向
# 正向 由外键所在表向外键关联表查询即为正向
# 反向 由外键关联表向外键所在表查询即为反向
多表查询
# 基于对象的跨表查询,即子查询,正向查询用字段,反向查询用小写表名
models.Book.objects.filter(pk=1).first().publish.name # 查图书表主键为一的书的出版社
models.Book.objects.filter(pk=1).first().authors.all() # 查符合条件的书籍的所有作者
pub_obj = models.Publish.objects.filter(name='东方出版社').first()
pub_obj.book_set.all() # 查询东方出版社出版的书籍
# 反向查询时,当查询结果有可能有多个时,需要_set.all(),一对一结果不需要
# 基于双下划线的跨表查询,即连表查询
models.Author.objects.filter(name='吴承恩').values(‘name','author_info__phone') #查询吴承恩手机号,返回QuerySet
# 可以将返回的QuerySet当做mysql中的结果表,返回的一个不可分对象当做一条记录。
# filter()中的字段名作为关键字不需用'',而values中的字段名需要''
# publish是虚拟字段,publish_id是django自动添加后缀在mysql生成的字段,.publish返回的是此外键关联的publish对象,而.values('publish_id')返回的是该条记录此字段的值
聚合查询
from django.db.models import Max,Min,Sum,Count,Avg # 一般情况下与分组查询一起使用
#单独使用聚合查询 aggregate
分组查询
# annotate
models.Book.objects.annotate(a_num=Count('authors')).values('name','a_num') #查询每本书的作者数量
print(models.Book.objects.aggregate(Avg('price'))
print(models.Book.objects.annotate(a_num=Count('authors')).values('name','a_num'))
print(models.Publish.objects.annotate(l_price=Min('book__price')).values('name','l_price'))
print(models.Book.objects.annotate(a_num=Count('authors')).filter(a_num__gt=1).values('name','a_num'))
print(models.Author.objects.annotate(amount=Sum('book__price')).values('name','amount'))
# 按照字段分组查询
models.Author.objects.values('price').annotate()
F与Q查询
# F('') 直接获取表中某字段的数据返回到代码位置
#
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(name=Concat(F('name'),Value('文学大师推荐')))
from django.db.models import Q
print(models.Book.objects.filter(Q(sold__gt=2000)|Q(price__lt=100)).values('name','sold','price')) # or=|,and=,,not=~
q = Q()
q.connector = 'or' # 设置条件连接关系
q.children.append(('sold__gt',2000)) # 设置比较条件,注意传入的是一个元组
q.children.append(('price__lt',100))
print(models.Book.objects.filter(q).values('name','sold','price'))
# 即SELECT `app01_book`.`name`, `app01_book`.`sold`, `app01_book`.`price` FROM `app01_book` WHERE (`app01_book`.`sold` > 2000 or `app01_book`.`price` < 100) LIMIT 21; args=(2000, Decimal('100'))
开启事务
# 事务的ACID:原子性,一致性,隔离性,持久性
# 事务的回滚rollback、确认commit
from django.db import transaction
with transaction.atomic():
# 内部所有ORM操作同属于一个事务
ORM常用字段及参数
AutoField #主键字段 primary_key=True
CharField #即varchar verbose字段注释,max_lenth长度
IntegerField #即int
BigIntegerField #即BigInt
DecimalField #max_digits,decimal_places
EmailField #varchar(254)
DateField #即Date auto_now/auto_now_add
DateTimeField #即DateTime
BooleanField #即布尔值,传False/True,存0/1
TextField #没有字数限制
FileField #upload_to ''
# 自定义字段
# 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):
# '''
# 返回真正的数据类型及约束条件
# :param connection:
# :return:
# '''
# return f'char({self.max_length})'
#null:即是否可以为空
#unique:是否唯一
#db_index:为字段建立索引
#default:设置字段默认值
#外键字段
#to_field:默认关联另一张表的主键字段
#on_delete:2.x一对多一对一需要手动设定级联
数据库查询优化
# ORM惰性查询,如果查询结果没有被使用,则不执行查询语句
# only/defer
models.Book.objects.only('name') # 返回包含name字段信息的对象组成的QuerySet,但仍可以.其他字段,只不过会重新查询数据库
models.Book.objects.defer('name') # 返回不包含name字段信息的对象组成的QuerySet,但仍可以.name字段,只不过会重新查询数据库
#only与defer一种是返回只包含参数字段对象组成的QuerySet,一种是返回不包含参数字段对象组成的QuerySet,如果要使用.字段访问信息,则当返回的QuerySet中没有此字段信息时需要重新查询数据库
# select_related(连表查询)/prefetch_ralated(子查询)
res = models.Book.objects.all()
for i in res:
print(i.publish.name) # 每循环一次都查询一次数据库
res = models.Book.objects.select_related('publish') # 只在这里连表查询一次数据库、参数只能是一对多一对一外键字段,只支持正向查询
for i in res:
print(i.publish.name) # 这里使用上面的连表查询结果,不用每次都查询数据库
res = models.Book.objects.prefetch_related('publish')
for i in res:
print(i.publish.name)
# 要结合具体需求,选择查询方法,select_related返回的对象结果可能过大,而prefetch_related查询的次数可能更多。
图书管理系统
# redirect参数可以直接传URL别名,但使用带正则的别名必须使用reverse()
# 见项目代码
choices参数
# 如果某个字段的值是可以列举完全的,那么通常会采用choices参数
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他')
)
gender = models.IntegerField(choices=gender_choices) # 该gender字段仍存数字,但是如果数字值在gender_choices内,那么可以轻松获取数字对应的真正内容,字段类型由元组内第一个参数类型决定
# gender字段可以存储不在gender_choices内的值,范围参考数据类型
# 获取对应信息 get_字段名_display
user_obj = models.User.objects.filter(pk=1).first()
user_obj.get_gender_display()
# 如果无对应值,则返回原值
MTV与MVC模型
# MTV,即models、templates、views
# MVC,即models、views、controller
多对多三种创建方式
class BooK
class Author
# 全自动:利用ORM自动创建中间表,可以使用ORM封装好的操作,但无法使用ORM拓展
authors = models.ManyToManyField(to='Author')
# 半自动:可以使用ORM正反向查询,但无法使用add、set、remove、clear四个方法,为了拓展性,一般半自动
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
class Book
authors = models.ManyToManyField(to='Author',through='Book2Author',through_field=('book','author')) # 元组顺序由中间表查询本表、被连接表的键决定,可以判断为先自后它
# 全手动:手动创建表,无法使用ORM封装好的操作,容易拓展
class Book_Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')