1 django中如何开启事务
"""
事务
特性:ACID
A原子性 不可分割的最小单位
C一致性 事务间的操作是一致的,同时成功或失败 跟原子性是相辅相成的
I隔离性 事物之间互不干扰
D持久性 事物一旦确认永久生效
事务的回滚
rollback
事务的确认
commit
"""
# 开启事物有2中方式:with上下文的形式、装饰器的形式,
# 事务 transaction n. 交易;事务;
# eg:以with上下文的形式为例
from django.db import transaction
try:
with transaction.atomic():
# sql1
# sql2
...
# 在with代码快内书写的所有orm操作都是属于同一个事务
except Exception as e:
print(e)
print('执行其他操作')
transaction.rollback() # 回滚
transaction.commit() # 提交
2 orm中常用字段及参数
2.1 常用字段
# 更多字段
直接参考博客:http://www.liuqingzheng.top/python/Django%E6%A1%86%E6%9E%B6/10-%E6%A8%A1%E5%9E%8B%E5%B1%82-%E5%B8%B8%E7%94%A8%E9%9D%9E%E5%B8%B8%E7%94%A8%E5%AD%97%E6%AE%B5%E5%92%8C%E5%8F%82%E6%95%B0/
# AutoField 主键字段
int自增列 必须填入参数 primary_key=True
当model中如果没有自增列,则自动会创建一个列名为id的列
# CharField -varchar
verbose_name 字段的注释
max_length 字符长度
# IntegerField -int
整数类型,范围在 -2147483648 to 2147483647
# BigIntegerField bigint
# DecimalField
max_digits=8
decimal_places=2
# EmailFiled -varchar(254)
# 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 - 布尔值类型
该字段传布尔值(False/True) 数据库里面存0/1
# TextField - 文本类型
该字段可以用来存大段内容(文章、博客...) 没有字数限制 eg:文章内容
# FileField - 字符串类型
upload_to = "/data"
给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中
eg: /data/a.txt
2.2 自定义字段 (了解)
# 自定义字段
# Django中CharField对应MySQL的varchar类型,没有设置对应char类型的字段
# eg:自定义对应于数据库的char类型
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):
"""
返回真正的数据类型及各种约束条件
"""
return 'char(%s)'%self.max_length
# 自定义字段使用
myfield = MyCharField(max_length=16,null=True)
2.3 字段参数
# null 是否为空
null=True 该字段可以为空
# unique 是否唯一
unique=True 该字段为唯一值,不能重复
ForeignKey(unique=True) === OneToOneField()
# 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者 但是前者也能用
# db_index
db_index=True 则代表着为此字段设置索引
# default
为该字段设置默认值。default=None
# DateField和DateTimeField的参数
# auto_now_add
创建数据记录的时候会把当前时间添加到数据库
# auto_now
每次更新数据记录的时候会更新该字段
2.4 关系字段及参数
##### ForeignKey关系字段
ForeignKey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方
可以和其他表做关联关系,同时也可以和自身做关联关系
# 字段参数
# to
to='表名' 或 表名 设置要关联的表 # 推荐字符串的形式
# to_field
to_field='关联表的字段名'
设置要关联表的字段 默认不写 关联的就是另外一张的主键字段
# related_name 给外键字段起别名 用于反向
反向操作时,使用的字段名,用于代替原反向查询时的'小写表名_set'
# related_query_name
反向查询操作时,使用的连接前缀,用于替换表名
# on_delete
级联关系,当删除关联表中的数据时,当前表与其关联表的行为。
# django1.X 默认是级联更新和级联删除,删除一个表数据,关联表的数据也删除
# django2.X及以上版本 需要你自己指定外键字段的级联关系
# 参数:
models.CASCADE 级联更新和级联删除
# eg: 作者没了,详情也没 (实际工作中,基本不会级联更新和级联删除,慎用!!!)
models.DO_NOTHING 删除关联数据,什么都不做 (会引发错误IntegrityError,id值不存在了,什么都不做,报完整性错误)
# eg: 出版社没了,书还是那个出版社出版
models.SET_NULL 删除关联数据,与之关联的值设置为null(前提FK字段需要设置可为空)
# eg: 部门没了,员工没有部门(空部门) null=True,
models.SET_DEFAULT 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
# eg: 部门没了,员工进入默认部门(默认值) default=0,
# db_constraint # constraint n. 约束
是否在数据库中创建外键约束,默认为True
为False时, 表明外键关系只存在逻辑上的关联,没有实质的外键联系,增删时不会受外键影响,orm查询操作也不影响
# 表之间 断关联
1.表之间没有外键关联,但是有外键逻辑上关联(有充当外键的字段)
2.断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(因为不需要做外键表的校验),且不影响增删改查操作
3.断关联一定要通过逻辑,保证表之间数据的安全,不要出现脏数据,自己代码控制
# 实际工作中,通常外键表之间设置db_constraint=False,因为若有外键约束的存在,关联数据增删改时有约束限制
##### OneToOneField关系字段
OneToOneField 一对一字段
通常一对一字段用来扩展已有字段
多用在当一张表的不同字段查询频次差距过大的情况下
将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系
# 字段参数
同ForeignKey参数基本一样
##### ManyToManyField关系字段
ManyToManyField
用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系
# 字段参数
其他同ForeignKey参数基本一样
# symmetrical
仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。
# through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
# through_fields
设置关联的字段
# db_table
默认创建第三张表时,数据库中表的名称
2.5 元信息
ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息
# 主要字段如下:
# db_table
ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名
# index_together
联合索引
# unique_together
联合唯一索引
# ordering
指定默认按什么字段排序
只有设置了该属性,我们查询到的结果才可以被reverse()
# verbose_name admin中显示的表名称
verbose_name='哈哈'
# verbose_name加s
verbose_name_plural=verbose_name
2.6 choices参数
"""
用户表
性别
学历
工作经验
是否结婚
是否生子
客户来源
...
针对某个可以列举完全的可能性字段,我们应该如何存储
格式:
字段_choices = (
(),
(),
()
)
只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数
"""
# models.py
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
# 性别_选项
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'),
)
gender = models.IntegerField(choices=gender_choices)
"""
该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内
那么可以非常轻松的获取到数字对应的真正的内容
"""
score_choices = (
('A','优秀'),
('B','良好'),
('C','及格'),
('D','不合格'),
)
# 保证字段类型跟列举出来的元祖第一个数据类型一致即可(******)
score = models.CharField(choices=score_choices,null=True)
# test.py
from app01 import models
# 验证:
# 1.若gender字段存的数字,不在上述元祖列举的范围内容,会如何?
:没有列举出来的数字也能存(范围还是按照字段类型决定)
# 2.如果在 如何获取对应的中文信息?
:固定写法 get_字段名_display()
# 存
models.User.objects.create(username='jason',age=18,gender=1)
models.User.objects.create(username='egon',age=85,gender=2)
models.User.objects.create(username='tank',age=40,gender=3)
# 存的时候 没有列举出来的数字也能存(范围还是按照字段类型决定)
models.User.objects.create(username='tony',age=45,gender=4)
# 取
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender) # 1
# 只要是choices参数的字段 如果你想要获取对应信息 固定写法 get_字段名_display()
print(user_obj.get_gender_display()) # 男
user_obj = models.User.objects.filter(pk=4).first()
# 如果没有对应关系 那么字段是什么还是展示什么
print(user_obj.get_gender_display()) # 4
# 实际项目案例
# CRM相关内部表
class School(models.Model):
"""
校区表
如:
北京沙河校区
上海校区
"""
title = models.CharField(verbose_name='校区名称', max_length=32)
def __str__(self):
return self.title
class Course(models.Model):
"""
课程表
如:
Linux基础
Linux架构师
Python自动化开发精英班
Python自动化开发架构师班
Python基础班
go基础班
"""
name = models.CharField(verbose_name='课程名称', max_length=32)
def __str__(self):
return self.name
class Department(models.Model):
"""
部门表
市场部 1000
销售 1001
"""
title = models.CharField(verbose_name='部门名称', max_length=16)
code = models.IntegerField(verbose_name='部门编号', unique=True, null=False)
def __str__(self):
return self.title
class UserInfo(models.Model):
"""
员工表
"""
name = models.CharField(verbose_name='员工姓名', max_length=16)
email = models.EmailField(verbose_name='邮箱', max_length=64)
depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code")
user=models.OneToOneField("User",default=1)
def __str__(self):
return self.name
class ClassList(models.Model):
"""
班级表
如:
Python全栈 面授班 5期 10000 2017-11-11 2018-5-11
"""
school = models.ForeignKey(verbose_name='校区', to='School')
course = models.ForeignKey(verbose_name='课程名称', to='Course')
semester = models.IntegerField(verbose_name="班级(期)")
price = models.IntegerField(verbose_name="学费")
start_date = models.DateField(verbose_name="开班日期")
graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, )
teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo',limit_choices_to={'depart':1002})
tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo',related_name="class_list",limit_choices_to={'depart':1006})
def __str__(self):
return "{0}({1}期)".format(self.course.name, self.semester)
class Customer(models.Model):
"""
客户表
"""
qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一')
name = models.CharField(verbose_name='学生姓名', max_length=16)
gender_choices = ((1, '男'), (2, '女'))
gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)
education_choices = (
(1, '重点大学'),
(2, '普通本科'),
(3, '独立院校'),
(4, '民办本科'),
(5, '大专'),
(6, '民办专科'),
(7, '高中'),
(8, '其他')
)
education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, )
graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True)
major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True)
experience_choices = [
(1, '在校生'),
(2, '应届毕业'),
(3, '半年以内'),
(4, '半年至一年'),
(5, '一年至三年'),
(6, '三年至五年'),
(7, '五年以上'),
]
experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices)
work_status_choices = [
(1, '在职'),
(2, '无业')
]
work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True,
null=True)
company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True)
salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True)
source_choices = [
(1, "qq群"),
(2, "内部转介绍"),
(3, "官方网站"),
(4, "百度推广"),
(5, "360推广"),
(6, "搜狗推广"),
(7, "腾讯课堂"),
(8, "广点通"),
(9, "高校宣讲"),
(10, "渠道代理"),
(11, "51cto"),
(12, "智汇推"),
(13, "网盟"),
(14, "DSP"),
(15, "SEO"),
(16, "其它"),
]
source = models.SmallIntegerField('客户来源', choices=source_choices, default=1)
referral_from = models.ForeignKey(
'self',
blank=True,
null=True,
verbose_name="转介绍自学员",
help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",
related_name="internal_referral"
)
course = models.ManyToManyField(verbose_name="咨询课程", to="Course")
status_choices = [
(1, "已报名"),
(2, "未报名")
]
status = models.IntegerField(
verbose_name="状态",
choices=status_choices,
default=2,
help_text=u"选择客户此时的状态"
)
consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultanter',limit_choices_to={'depart':1001})
date = models.DateField(verbose_name="咨询日期", auto_now_add=True)
recv_date = models.DateField(verbose_name="当前课程顾问的接单日期", null=True)
last_consult_date = models.DateField(verbose_name="最后跟进日期", )
def __str__(self):
return self.name
class ConsultRecord(models.Model):
"""
客户跟进记录
"""
customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer')
consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo',limit_choices_to={'depart':1001})
date = models.DateField(verbose_name="跟进日期", auto_now_add=True)
note = models.TextField(verbose_name="跟进内容...")
def __str__(self):
return self.customer.name + ":" + self.consultant.name
class Student(models.Model):
"""
学生表(已报名)
"""
customer = models.OneToOneField(verbose_name='客户信息', to='Customer')
class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True)
emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True)
def __str__(self):
return self.customer.name
class ClassStudyRecord(models.Model):
"""
上课记录表 (班级记录)
"""
class_obj = models.ForeignKey(verbose_name="班级", to="ClassList")
day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字")
teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo',limit_choices_to={'depart':1002})
date = models.DateField(verbose_name="上课日期", auto_now_add=True)
course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True)
def __str__(self):
return "{0} day{1}".format(self.class_obj, self.day_num)
class StudentStudyRecord(models.Model):
'''
学生学习记录
'''
classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord")
student = models.ForeignKey(verbose_name="学员", to='Student')
record_choices = (('checked', "已签到"),
('vacate', "请假"),
('late', "迟到"),
('noshow', "缺勤"),
('leave_early', "早退"),
)
record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
score_choices = ((100, 'A+'),
(90, 'A'),
(85, 'B+'),
(80, 'B'),
(70, 'B-'),
(60, 'C+'),
(50, 'C'),
(40, 'C-'),
(0, ' D'),
(-1, 'N/A'),
(-100, 'COPY'),
(-1000, 'FAIL'),
)
score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True)
homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True)
def __str__(self):
return "{0}-{1}".format(self.classstudyrecord, self.student)
"""
chocies参数使用场景是非常广泛的
"""
3 多对多创建三种方式
# 全自动: 利用orm自动帮我们创建第三张关系表
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
name = models.CharField(max_length=32)
"""
优点:代码不需要你写 非常的方便 还支持orm提供操作第三张关系表的方法...
不足之处:第三张关系表的扩展性极差(没有办法额外添加字段...)
"""
# 纯手动: 自行创建第三张表 分别通过外键关联书和作者
class Book(models.Model):
name = models.CharField(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提供的简单的方法
不建议你用该方式
'''
# 半自动: 自己创建第三张表,并通过ManyToManyField指定关联 ---> 中介模型
class Book(models.Model):
name = 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)
# books = models.ManyToManyField(to='Book',
# through='Book2Author',
# through_fields=('author','book')
# )
class Book2Author(models.Model):
book = models.ForeignKey(to='Book') # 外键会自动在 字段名 后加 '_id'
author = models.ForeignKey(to='Author')
"""
中介模型:through_fields字段先后顺序
判断的本质:
通过第三张表查询对应的表 需要用到哪个字段就把哪个字段放前面
你也可以简化判断
当前表是谁 就把对应的关联字段放前面
半自动:可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法
"""
# 总结:
你需要掌握的是全自动和半自动
为了扩展性更高 一般我们都会采用半自动 (写代码要给自己留一条后路)
4 数据库查询优化
# 一、only与defer
目的:减缓查询复杂度,提升查询优化!
不会将所有字段全部都一次性执行查询,需要某个字段再执行相应的语句 # 通常单表
# 二、select_related与prefetch_related
目的:减少执行sql语句的命令条数,提升查询优化! # 通常多表
# 三、extra
目的:在查询条件复杂的情况下,通过 'QuerySet生成的sql语句中添加新的条件子句' 方式,额外指定新的查询条件
注意:
extra方法的核心是 让你orm的语句,额外再加上 原生的sql语句
但若是项目需要切换不同的sql数据库或者引擎,会导致可移植性的问题,原生的sql语句并不一定完全通用嘛 # 尽量避免使用
# 四、queryset 查询集的特性:
惰性查询,是惰性执行的。
创建 查询集 不会带来任何数据库的访问。你可以将过滤器保持一整天,直到 查询集 需要求值时,Django才会真正运行这个查询
如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数
那么orm会自动识别 直接不执行
# eg:
res = models.Book.objects.all() # 返回的是queryset 查询集 对象,单独该命令,不会执行的
print(res) # 确定要使用数据了,才会执行,走数据库命令
res = models.Book.objects.get(name="红楼梦") # 返回的是具体的book对象,orm直接执行sql语句
4.1 only与defer
# only与defer 和 all、values 对比来看
# 1 .all() :会将表的所有字段都一次性通过sql命令先查出来,后续再访问字段值,就不会执行sql命令了!
# 想要获取书籍表中所有书的名字
res = models.Book.objects.all() # 会将表的所有字段都一次性通过sql命令先查出来
# print(res) # # <QuerySet [<Book: 三国演义爆款>, <Book: 红楼梦爆款>]> 此时是 queryset对象,看作是 列表套对象 的格式
for d in res:
print(d.title) # 后续再访问字段值,就不会执行sql命令了!
# 2 .values():会将括号内的字段都一次性通过sql命令先查出来,后续再访问字段值,就不会执行sql命令了!
# 想要获取书籍表中所有书的名字
res = models.Book.objects.values('title') # 只将'title'字段,通过sql命令查出来
# print(res) # <QuerySet [{'title': '三国演义爆款'}, {'title': '三国演义爆款'}]> 此时是 queryset对象,看作是 列表套字典 的格式
for d in res:
print(d.get('title'))
# 测试结果
:res对应的sql语句,也只将字段'title'拿出来了,没办法获取其他字段的值!
:且后续不能直接通过 res.title 获取到值,需要 字典获取值 的方式 才能拿到值!!!
# 3 .only('字段名') : 只会将only中的字段通过sql命令查出来,后续再访问该字段值,不会执行sql命令了
: 但若是访问表的其他字段值 (only括号内没有的字段), 就会重新走sql命令进行查询
# 你给我实现获取到的是一个数据对象 然后点title就能够拿到书名 并且没有其他字段
res = models.Book.objects.only('title') # 此时sql语句,只会查表的 'title'字段,没有其他字段的信息
# print(res) # <QuerySet [<Book: 三国演义爆款>, <Book: 红楼梦爆款>]> 此时也是 queryset对象,看作是 列表套对象 的格式
# 后续能直接通过 res.title 获取到值!!!
for i in res:
print(i.title) # 点击only括号内的字段 就不会再走数据库
print(i.price) # 点击only括号内没有的字段 会重新走数据库查询而all不需要走了
# 4 .defer('字段名') : 会将除了defer中的字段,全都先通过sql命令查出来,后续再访问该字段值,会重新走sql命令进行查询
: 但若是访问表的其他字段值 (defer括号内没有的字段), 就不会走sql命令
: defer v. 延迟
res = models.Book.objects.defer('title') # 对象除了没有title属性之外其他的都有
for i in res:
print(i.price)
# defer与only刚好相反
defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据库
而如果查询的是非括号内的字段 则不需要走数据库了
# select_related与prefetch_related 跟跨表操作有关
# 1. 一般情况下 查询
res = models.Book.objects.all() # 一条查询book表的sql语句命令
for i in res:
print(i.publish.name) # 每循环一次就要走执行一次sql语句命令,查询publish表
# 2. select_related() 联表查询
res = models.Book.objects.select_related('publish') # 执行的是一句sql命令 联表查询 INNER JOIN
for i in res:
print(i.publish.name) # 后续关于两个表相关的数据,都不会执行sql语句命令了!
"""
select_related内部直接先将book与publish表连起来 然后一次性将大表里面的所有数据 全部封装给查询出来的对象
这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了
注意:select_related括号内只能放外键字段 (外键关系:一对多 一对一) 多对多 不行 !!!!
"""
# 3. prefetch_related() 子查询 prefetch v. 预先载入
res = models.Book.objects.prefetch_related('publish') # 执行的是两句sql命令 子查询 将第一条查询结果作为第二条查询的条件
for i in res:
print(i.publish.name) # 后续关于相关的数据,都不会执行sql语句命令了!
"""
prefetch_related该方法内部其实就是子查询 将子查询查询出来的所有结果 也给你封装到对象中
给你的感觉好像也是一次性搞定的
注意:prefetch_related 主要用于 多对多 一对多
"""
### 1 前提:
有些情况下,Django的ORM查询语法,难以简单的表达 复杂的sql语句
Django提供了 extra() 'QuerySet修改机制',它能在 QuerySet生成的SQL从句中,注入新的原生sql子句
### 2 注意: 尽量少使用
这种额外的方式,对不同的数据库引擎可能存在移植性问题
因为实际是 在orm语句的基础上,额外再加上 显式的书写SQL原生语句
### 3 extra 参数 至少一个
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 3.1 select参数
可在 原本SELECT从句中添加其他字段信息,是一个字典 eg: {'字段属性名': 'SQL从句'}
# eg:
queryResult = models.Article.objects
.extra(select={'is_recent': "create_time > '2017-09-05'"})
# 结果:
结果集中每个Article对象都有一个额外的属性is_recent, 它是一个布尔值
表示 Article对象的create_time 是否晚于2017-09-05
# 3.2 where 、tables 参数
可以使用 where 定义显式SQL的'WHERE'子句
可以使用 tables 手动将表添加到SQL的'FROM'子句
where、tables 都接受字符串列表。所有 where 参数均为'与'的搜索条件
# eg:
queryResult = models.Article.objects
.extra(where=['nid in (3,4) OR title like "py%" ','nid>2'])
# 结果:
相当于 在原本orm查询后的 select从句,再加了一个 sql
'条件1:nid 为3或4,或者 title以py开头'、'条件2:nid 大于2'
两个条件同时成立的 where子句
5 数据库设计三大范式
# 自我总结
为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则,在关系型数据库中这种规则就称为范式。
# 1.第一范式(确保每列保持原子性)
每一列属性字段都是不可再分的属性值,确保每一列的原子性
eg:个人信息表中,含有电话 或 地址字段 (不符合第一范式)
因为,电话可以继续拆分:个人电话、家庭电话、工作电话等;地址也可以继续拆分:省、市、地区等
# 2.第二范式(确保表中的每列都和主键相关)
属性字段完全依赖于主键 第二范式在第一范式的基础之上更进一层
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关
主要针对联合主键而言,比如有两个主键,不能存在这样的属性,它只依赖于其中一个主键
通俗解释:任意一个字段都只依赖表中的主键列,主键列与非主键列遵循完全函数依赖关系,也就是完全依赖
eg:订单详细表中有(订单详细编码,订单详细名,订单编号,订单名,订单类型,商品编码,商品名,商品单价等)
其中可以看出:
订单详细名 → 订单详细编码
订单名,订单类型 → 订单编号
商品名,商品单价→ 商品编码
其中 → 符号代表依赖,由于上述只有部分列依赖于主键,违背了第二范式
可以对其进行拆分成 订单详细表,订单表,商品表三个表,通过主外键进行相互关联
# 3.第三范式(确保每列都和主键列直接相关,而不是间接相关)
数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系
像:a-->b-->c 属性之间含有这样的关系,是不符合第三范式的
通俗来说:就是外键,必须是各自单独分开的表,不能直接在一起
eg:学生信息表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)
其中可以看出:
学号--> 所在院校 --> (院校地址,院校电话)
正确应该拆开来,如下:
(学号,姓名,年龄,性别,所在院校)-->(所在院校,院校地址,院校电话)
# 总结:
三大范式只是一般设计数据库的基本理念,可以建立冗余较小、结构合理的数据库
如果有特殊情况,当然要特殊对待,数据库设计最重要的是看需求跟性能,需求>性能>表结构
所以不能一味的去追求范式建立数据库
6 MTV与MVC模型
# MTV:Django号称是MTV模型
M:models
T:templates
V:views
# MVC:其实django本质也是MVC
M:models
V:views
C:controller (urls.py...做分发)
# vue框架:MVVM模型
7 图书管理系统
7.1 图书增删改查
# app01.view.py
from django.shortcuts import render,redirect,HttpResponse
from app01 import models
# Create your views here.
def home(request):
return render(request,'home.html')
def book_list(request):
# 先查询出所有的书籍信息 传递给html页面
book_queryset = models.Book.objects.all()
return render(request,'book_list.html',locals())
def book_add(request):
if request.method == 'POST':
# 获取前端提交过来的所有数据
title = request.POST.get("title")
price = request.POST.get("price")
publish_date = request.POST.get("publish_date")
publish_id = request.POST.get("publish")
authors_list = request.POST.getlist("authors") # [1,2,3,4,]
# 操作数据库存储数据
# 书籍表
book_obj = models.Book.objects.create(title=title,price=price,publish_date=publish_date,publish_id=publish_id)
# 书籍与作者的关系表
book_obj.authors.add(*authors_list)
# 跳转到书籍的展示页面
"""
redirect括号内可以直接写url
其实也可以直接写别名
但是如果你的别名需要额外给参数的话,那么就必须使用reverse解析了
"""
return redirect('book_list')
# 先获取当前系统中所有的出版社信息和作者信息
publish_queryset = models.Publish.objects.all()
author_queryset = models.Author.objects.all()
return render(request,'book_add.html',locals())
def book_edit(request,edit_id):
# 获取当前用户想要编辑的书籍对象 展示给用户看
edit_obj = models.Book.objects.filter(pk=edit_id).first()
if request.method == 'POST':
title = request.POST.get("title")
price = request.POST.get("price")
publish_date = request.POST.get("publish_date")
publish_id = request.POST.get("publish")
authors_list = request.POST.getlist("authors") # [1,2,3,4,]
models.Book.objects.filter(pk=edit_id).update(title=title,
price=price,
publish_date=publish_date,
publish_id=publish_id
)
# 该第三张关系表
edit_obj.authors.set(authors_list)
return redirect('book_list')
publish_queryset = models.Publish.objects.all()
author_queryset = models.Author.objects.all()
return render(request,'book_edit.html',locals())
def book_delete(request,delete_id):
# 简单粗暴 直接删除
models.Book.objects.filter(pk=delete_id).delete()
# 直接跳转到展示页
return redirect('book_list')