ORM查询
1、只要是queryset对象就可以无限制调用Queryset对象的方法:
res = models.User.objects.filter().filter().update()....
2、只要是queryset对象就可以通过下面的语句获得sql语句:
print(res.query)
3、如果不是queryset对象,也可以获得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', }, } }
单独测试Django部分功能
当测试Django的时候,我们往往只需要测试部分功能,启动整个Django显得太慢,太大,因此我们可以书写测试脚本:
步骤如下:
1、在任意目录下创建一个py文件进行书写,或者直接在app下的tests.py
文件中书写以下代码:
# 以下代码可以去manage.py文件中复制 import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings") import django django.setup()
2、示范,例如我想对模板层进行测试,代码如下:
import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings") import django django.setup() # 导入models from app01 import models # 调用models相关功能 models.Movie.objects.all()
单表查询
在对models进行测试的时候,我们需要配置数据库的相关参数(settings.py)以及在__init__.py
中导入pymysql,在models.py文件中创建我们的模型表,步骤如下:
1、在models.py中创建模型表
class Movie(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) # auto_now_add=True 当对数据进行创建的时候会自动添加时间 publish_time = models.DateField(auto_now_add=True)
2、执行迁移数据库命令
python manage.py makemigrations python manage.py migrate
基础的16条
- create() 返回值是当前创建的数据对象本身
- all() queryset对象
- filter() queryset对象
- update() 更新数据
- delete() 删除数据
- first() 取第一子元素
- last() 取最后一个子元素
- get() 数据对象,不推荐使用,条件不存在直接报错
- values() queryset对象 看似:列表套字典
- values_list() queryset对象 看似:列表套元组
- order_by() 排序
- count() 计数
- exclude() 排除某个字段在外
- exists() 判断结果是否存在数据,返回的是bool值
- reverse() 反转
- distinct() 去重,必须是完全重复的数据
from django.test import TestCase # Create your tests here. import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mystile.settings") import django django.setup() from test_func import models from datetime import date ctime = date.today() # 还可以直接传入日期时间对象 # 1、create() 创建数据,得到的返回值就是创建数据的对象本身,可以直接使用res res = models.Movie.objects.create(title='山楂树之恋', price=999, publish_time=ctime) # 2、all() 查询表中的所数据,返回的是queryset对象, 类似列表套对象 res = models.Movie.objects.all() print(res) # 3、filter() 按照指定条件去查询,返回的是queryset对象,类似列表套对象 res = models.Movie.objects.filter(id=1) print(res) # 只要是queryset对象,就可以通过 点query 的方式查看sql语句 res = models.Movie.objects.filter(title='山楂树之恋') print(res.query) # 4、get() 返回的是对象本身,不推荐使用,因为当查询条件不存在的时候会报错 res = models.Movie.objects.get(pk=1) print(res) # 5、values() 获取指定字段对应的数据,列表套字典 QuerySet对象 res = models.Movie.objects.values('title', 'price', 'publish_time') print(res) # 6、values_list() 获取指定字段对应的数据,元组套字典 QuerySet对象 res = models.Movie.objects.values_list('title', 'price') print(res) # 7、first() 获取第一个元素对象,返回的是数据对象 res = models.Movie.objects.filter().first() print(res) # 8、last() 获取最后一个元素对象,返回的是数据对象 res = models.Movie.objects.filter().last() print(res.title) # 9、update() 更新数据 返回值是受影响的行数 res = models.Movie.objects.filter(pk=2).update(title='大鱼海棠') print(res) # 10、delete() 删除数据 返回值(1, {'app01.Movie': 1}) 受影响的表及行数 res = models.Movie.objects.filter(pk=2).delete() print(res) # 11、count() 统计数据条数 res = models.Movie.objects.count() print(res) res = models.Movie.objects.filter(pk=2).count() print(res) # 12、order_by() 按照指定字段排序 res = models.Movie.objects.order_by('price') # 默认升序 res1 = models.Movie.objects.order_by('-price') # 降序 print(res) print(res1) # 13、exclude() 排除什么什么之外的数据 res = models.Movie.objects.exclude(pk=1).first() print(res.title) # 14、exists() 判断查询是够有数据,返回bool值 res = models.Movie.objects.filter(pk=10000).exists() print(res) # 15、reverse() 反转 res = models.Movie.objects.order_by('price').reverse() print(res) # 16、distinct() 去重:去重的前提,必须是由完全一样的数据才能去重 # 因此,这里要注意将django自动创建的主键排除在外 res = models.Movie.objects.values('title', 'price').distinct() print(res)
双下划线查询
- __gt 大于
- __lt 小于
- __gte 大于等于
- __lte 小于等于
- __in 或
- __range 某个范围内,顾头也顾尾
模糊查询:
- __ contains 区分大小写
- __icontains 忽略大小写
年月日:
- create_time__year
- create_time__month
双下划线查询,一般用于括号内,对数据进一步进行限制,比如求商品的价格大于200的...
# 双下划线查询 # 1、查询价格大于200的电影 res = models.Movie.objects.filter(price__gt=200) print(res) # 2、查询价格小于500的电影 res = models.Movie.objects.filter(price__lt=500) print(res) # 3、查询价格大于等于200的电影 res = models.Movie.objects.filter(price__gte=200) print(res) # 4、查询价格小于等于400的电影 res = models.Movie.objects.filter(price__lte=500) print(res) # 5、查询价格在200到500之间的电影, 包括200到500 res = models.Movie.objects.filter(price__in=(200, 500)) # 6、查询价格是100,或66,或521 res = models.Movie.objects.filter(price__in=[100, 66, 521]) # 7、查询电影名字中包含字符“山楂树”的电影 ''' 模糊查询: 关键字:like 关键符:%和- ''' res = models.Movie.objects.filter(title__contains='山楂树') # 区分大小写 res = models.Movie.objects.filter(title__icontains='山楂树') # 多加i可以忽略大小写 print(res) # 8、查询2020年出版的电影 res = models.Movie.objects.filter(publish_time__year=2020) print(res) # 9、查询是1月份出版的电影 res = models.Movie.objects.filter(publish_time__month=1) print(res)
多表查询
图书管理系统表创建
针对多表查询,我们需要添加更多的表,步骤如下:
1、models.py中创建模型表
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) # 该字段新增数据自动添加 无需考虑 publish_time = models.DateField(auto_now_add=True) # 出版社 一对多(一个出版社可以出版多个书,一本书不可以被多个出版社出版) 外键字段建在多的一方 publish = models.ForeignKey(to='Publish') # 作者 多对多关系 # 一本书可以有多个作者,一个作者也可以写多本书 authors = models.ManyToManyField(to='Author') class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=255) class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() # 作者详情 一对一关系 author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.BigIntegerField() addr = models.CharField(max_length=255)
2、执行数据库迁移命令
python manage.py makemigrations python manage.py migrate
外键字段的增删改查
一对多
- publish 传数据对象
- publish_id 传数据主键值
# 1、增 直接写实际的表字段 publish_id models.Book.objects.create(title='三国演义', price=123.23, publish_id=2) # 2、增 先查出来要绑定的出版社对象 publish_obj = models.Publish.objects.get(pk=1) # 将查出来的对象赋值给publish models.Book.objects.create(title='水浒传', price=66.6, publish=publish_obj) # 查 # 在下面的多表查询中 # 改 # 第一种方法 models.Book.objects.filter(pk=2).update(publish_id=3) # 第二种方法,依旧是拿对象赋值 publish_obj = models.Publish.objects.get(pk=3) models.Book.objects.filter(pk=2).update(publish=publish_obj) # 删 ''' 外键字段在1.X版本中默认就是级联更新级联删除的 2.X版本中,则需要你自己手动指定 百度一大堆 当没有级联删除和级联更新的情况下,只有删除关联表中的数据,才能删除和修改被关联表中的数据 当有级联删除和级联更新的情况下,修改和删除被关联表中的数据, 关联表中的数据也会跟着被删除和修改 '''
多对多
add()
add 专门给多对多种的第三张虚拟表添加数据,括号内可以传数字,也可以穿对象,并且都支持传多个。
# 多对多 # 1、给书籍绑定作者关系 add book_obj = models.Book.objects.filter(pk=2).first() # 书籍和作者的关系是由第三张表决定,那就意味着需要操作第三张表 print(book_obj.authors) # 书籍对象.虚拟字段authors就类似已经跨到了书籍和作者的第三张表 book_obj.authors.add(1) # 给书籍绑定一个主键为1的作者 book_obj.authors.add(2, 3) # 第二种方法,直接添加对象 book_obj = models.Book.objects.filter(pk=3).first() author_obj = models.Author.objects.get(pk=1) author_obj1 = models.Author.objects.get(pk=3) book_obj.authors.add(author_obj) book_obj.authors.add(author_obj, author_obj1)
remove()
专门给第三张关系表移除数据,括号内既可以传数字也可以传对象,
并且都支持传多个。
book_obj = models.Book.objects.filter(pk=3).first() book_obj.authors.remove(1) book_obj.authors.remove(2, 3) author_obj = models.Author.objects.get(pk=1) author_obj1 = models.Author.objects.get(pk=3) book_obj.authors.remove(author_obj) book_obj.authors.remove(author_obj, author_obj1)
set()
修改书籍与作者的关系
括号内支持传数字和对象,但是需要是可迭代对象
book_obj = models.Book.objects.filter(pk=3).first() book_obj.authors.set((3,)) book_obj.authors.set((1, 2)) authors_obj = models.Author.objects.get(pk=1) authors_obj1 = models.Author.objects.get(pk=2) book_obj.authors.set((authors_obj,)) book_obj.authors.set([authors_obj, authors_obj1])
clear()
清空关系
不需要任何参数
book_obj = models.Book.objects.filter(pk=3).first() book_obj.authors.clear() # 去第三张表中清空书籍为1的所有书籍
跨表查询
跨表查询的方式
1、子查询
将一张表的查询结果当做另一张表的查询条件
2、链表查询
- inner join
- left join
- right join
- union
建议:在写sql语句或者orm语句的时候不要想着一次性将语句写完,一定要写一点查一点,再写一点
正反向的概念
正向:
跨表查询的时候 外键字段是否在当前数据对象中 如果在查询另外一张关系表 叫正向
反向:
如果不在叫反向
口诀:
正向查询按外键字段
反向查询按表名小写
基于对象的跨表查询(子查询)
正向查询的时候,当外键字段对应的数据可以有多个的时候需要加.all()
否则点外键字典即可获取到对应的数据对象
# 1、查询书籍pk为1的出版社名称 book_obj = models.Book.objects.filter(pk=2).first() print(book_obj.publish) print(book_obj.publish.name) print(book_obj.publish.addr) # 2、查询书籍pk为2的所有作者的姓名 book_obj = models.Book.objects.filter(pk=3).first() print(book_obj.authors) # test_func.Author.None print(book_obj.authors.all()) author_list = book_obj.authors.all() for author_obj in author_list: print(author_obj.name) # 3、查询作者pk为1的电话号码 author_obj = models.Author.objects.filter(pk=1).first() print(author_obj.author_detail) print(author_obj.author_detail.phone) print(author_obj.author_detail.addr)
基于对象的反向查询 表名小写是需要加_set.all()
- 一对多和多对多的时候需要加
- 一对一不需要
# 4、查询出版社名称为东方出版社出版过的书籍 publish_obj = models.Publish.objects.filter(name='东方出版社').first() print(publish_obj.book_set) # test_func.Book.None print(publish_obj.book_set.all()) # 5、查询作者为engon写过的书 author_obj = models.Author.objects.filter(name='engon').first() print(author_obj.book_set) # app01.Book.None print(author_obj.book_set.all()) # 6、查询手机号为120的作者姓名 author_detail_obj = models.AuthorDetail.objects.filter(phone=120).first() print(author_detail_obj.author) print(author_detail_obj.author.name) print(author_detail_obj.author.age)
基于双下划线的跨表查询(链表查询)
只要表之间有关系,你就可以通过正向的外键字段或者反向的表名小写,连续跨表查询
正向查询:先获取有外键字段的对象,通过对象的values内,使用外键字段__
到另外一张表中取值
反向查询:先获取另一张表的对象,在filter括号内,通过表名小写__到另外一张表中,得到另外一张表中的字段加限制条件,在.value的括号内,可以直接取本对象的value,以及通过双下划线__
取到另外一张表的value。
# 1、查询书籍pk为2的出版社名称 # 正向 # 写外键字段,就意味着你已经在外键字段管理的那张表中 res = models.Book.objects.filter(pk=2).values('publish__name') print(res) # 反向 # 拿出版过pk为1的书籍对应的出版社 res = models.Publish.objects.filter(book__pk=2) res = models.Publish.objects.filter(book__pk=2).values('name') print(res) # 2、查询书籍pk为2的作者姓名和年龄 # 正向 res = models.Book.objects.filter(pk=3).values('title', 'authors__name', 'authors__age') print(res) # 反向 res = models.Author.objects.filter(book__pk=2) # 拿出出版过书籍pk为1的作者 res = models.Author.objects.filter(book__pk=2).values('name', 'age', 'book__title') print(res) # 3、查询作者是engon的年龄和手机号 # 正向 res = models.Author.objects.filter(name='engon').values('age', 'author_detail__phone') # 反向 res = models.AuthorDetail.objects.filter(author__name='engon').values('author__age', 'phone') print(res) # 4、查询书籍pk为的2的作者的手机 # 正向 res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone') print(res) # 反向 res = models.AuthorDetail.objects.filter(author__book__pk=2).values('phone') print(res)
聚合查询
关键字:
aggregate
模块的导入:
from django.db.models import Max, Min, Avg, Count, Sum
- Max 查询最大值
- Min 查询最小值
- Avg 求平均值
- Count 统计
- Sum 求和
范例:
from django.db.models import Max, Min, Avg, Count, Sum # 查询所有书的平均价格 res = models.Book.objects.aggregate(avg_num=Avg('price')) print(res) # 查询价格最贵的书籍 res = models.Book.objects.aggregate(max_price=Max('price')) print(res) # 全部使用一遍 res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'), Count('pk'), Sum('price')) print(res)
分组查询
关键字:
annotate
模块导入:
from django.db.models import Max,Min,Sum,Count,Avg
# 1、统计每一本书的作者个数 res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num') print(res) # 2、统计出每一个出版社卖的最便宜的书的价格 res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'book__title', 'book__price') print(res) # 3、统计不止一个作者的书 res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title') print(res) # 4、查询各个作者出的书籍的总价格 res = models.Author.objects.annotate(author_num=Sum('book__price')).values('name', 'author_num') print(res) # 5、如何按照表中的某一指定字段分组 res = models.Book.objects.values('price').annotate() # 以价格字段进行分组 print(res)
F与Q查询
需要导入模块
from django.db.models import F, Q
F
可以获取到表中的某个字段对应的值
1.查询库存数大于卖出数的书籍 res = models.Book.objects.filter(kucun__gt=F('maichu')) print(res) 2.将所有书的价格提高100 res = models.Book.objects.update(price=F('price') + 100)
Q
能够改变查询的条件:
- and
- or
- not
Q能够改变查询的条件关系 and or not 1.查询书的名字是python入门或者价格是1000的书籍 res = models.Book.objects.filter(title='python入门',price=1000) # and关系 res = models.Book.objects.filter(Q(title='python入门'),Q(price=1000)) # 逗号是and关系 res = models.Book.objects.filter(Q(title='python入门')|Q(price=1000)) # |是or关系 res = models.Book.objects.filter(~Q(title='python入门')|Q(price=1000)) # ~是not关系
Q的高阶用法
Q的高阶用法,可以以用户输入的字符串变成可以查询的限制条件,因此多用于搜索框功能。
# res = models.Book.objects.filter('title'='python入门') q = Q() q.connector = 'or' # q对象默认也是and关系 可以通过connector改变or q.children.append(('title','python入门')) q.children.append(('price',1000)) res = models.Book.objects.filter(q) print(res)
models中常用字段
AutoField(primary_key=True) 主键字段 CharField(max_length=32) varchar(32) IntegerField() int BigIntergerField() bigint DecimalField() decimal EmailField() varchart(254) DateField() date DateTimeField() datetime auto_now:每次编辑数据的时候都会自动更新该字段时间 auto_now_add:创建数据的时候自动更新 BooleanField(Field) 给该字段传布尔值 会对应成 数字0/1 is_delete is_status is_vip TextField(Field) - 文本类型 存储大段文本 FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录,只存文件路径 upload_to = '指定文件路径' 给该字段传文件对象 文件会自动保存到upload_to指定的文件夹下 然后该字段存文件的路径
django默认建的字符串类型都是varchar,想要建立char字段,需要自定义char类型的字段,方法如下:
# modles.py from django.db.models import Field class RealCharField(Field): def __init__(self,max_length,*args,**kwargs): self.max_length = max_length # 拦截一个父类的方法 操作完之后 利用super调用父类的方法 super().__init__(max_length=max_length,*args,**kwargs) def db_type(self, connection): return 'char(%s)'%self.max_length # 使用自定义的char类型字段 class Movie(models.Model): textField = RealCharField(max_length=64)
字段内的关键性参数
null
null 用于表示某个字段可以为空
default
default 设置该字段的默认值
unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index
如果db_index=True 则代表着为此字段设置索引。
2.x版本级联更新和级联删除
django 1.x默认就是级联更新级联删除 django2.x需要你自己手动指定
on_delete=models.CASCADE
db_contraints = True
- 级联删除:models.CASCADE
当关联表中的数据删除时,该外键也删除 - 置空:models.SET_NULL
当关联表中的数据删除时,该外键置空,当然,你的这个外键字段得允许为空,null=True - 设置默认值:models.SET_DEFAULT
删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。
#级联删除情况 class UserToken(models): #级联删除,用户删除,它也删除 user = models.OneToOneField(to='User', on_delete=models.CASCADE, to_field='id') token = models.CharField(max_length=128, null=True) #置空情况 class Server(models.Model): server_type_choice = ( (1, "WEB"), (2, "存储"), (3, "缓存") ) server_type = models.IntegerField(choices=server_type_choice) hostname = models.CharField(max_length=32) port = models.IntegerField() business_unit = models.ForeignKey("BusinessUnit", on_delete= models.SET_NULL, null=True) #设置默认值 user = models.ForeignKey("UserInfo", on_delete= models.SET_DEFAULT, default=0)
- 两个不常用的
- PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。
- SET(): 自定义一个值,该值当然只能是对应的实体了
choices参数
当你数据能够被你列举完全,可以考虑使用choices参数,用数字来代替字符串存入数据库,可以节省硬盘空间。
class Userinfo(models.Model): username = models.CharField(max_length=32) gender_choices = ( (1,'男'), (2,'女'), (3,'其他'), ) gender = models.IntegerField(choices=gender_choices) # 该字段还是存数字 并且可以匹配关系之外的数字 record_choices = (('checked', "已签到"), ('vacate', "请假"), ('late', "迟到"), ('noshow', "缺勤"), ('leave_early', "早退"), ) record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64) user_obj = models.Userinfo.objects.get(pk=1) print(user_obj.username) print(user_obj.gender) # 针对choices参数字段 取值的时候 get_xxx_display() print(user_obj.get_gender_display()) # 针对没有注释信息的数据 get_xxx_display()获取到的还是数字本身 user_obj = models.Userinfo.objects.get(pk=4) print(user_obj.gender) print(user_obj.get_gender_display())
数据库查询优化
django orm的所有查询都是惰性查询,也就意味着,不使用查询出来的结果,sql语句并不会执行,例如:
res = models.Book.objects.all()
当我不引用和使用res的时候,这条语句是不会执行。
only与defer
only的作用:
括号内传字段,得到的结果是一个列表套数据对象,该内只含有括号内指定的字段属性
对象点该字段属性是不会走数据库的,但是你一旦点了非括号内的字段,也能够拿到数据,但是会走数据库查询
only将括号内的字段查询结果赋值给对象,对象点括号内的字段是不会走数据库查询的,超出括号内的字段,此时查询会走数据库。
res = models.Book.objects.only('title') # 這些對象内部只有title屬性 # print(res) for r in res: # print(r.title) print(r.price)
defer的作用:
括号内传字段,得到的结果是一个列表套数据对象,对象点查询defer括号内的字段属性,该字段属性会重复的走数据库,但是查询defer括号内没有的字段,就不会走数据库。
res = models.Book.objects.defer('title') for r in res: print(r.title)
selected_related与prefetch_related
selected_related:
内部是连表操作,先将关系表全部连接起来,之后再一次性查询出来,封装到对象中,数据对象之后在获取任意表中数据的时候都不需要再走数据库了,因为全部封装成了对象的属性。
select_related括号内只能传外键字段,并且不能是多对多字段,只能是一对多和一对一
select_related(外键字段1__外键字段2__外键字段3.....)
res = models.Book.objects.select_related('authors') for r in res: print(r.publish.name) print(r.publish.addr)
prefetch_related
看起来像是连表操作,但内部是子查询,内部通过子查询将外键关联表中的数据全部封装到对象中,之后点当前对象表或外键关联表中的字段也都不需要走数据库了。
prefetch_related res = models.Book.objects.prefetch_related('publish') # print(res) for r in res: print(r.publish.name)
优缺点比较:
select_related连表操作,好处在于只走一次sql查询,但缺点是耗时耗在 连接表的操作上。
prefetch_related子查询,走两次sql查询,耗时耗在查询次数上。
django orm如何开启事务操作
事务介绍
事务的四大特性(ACID):
原子性:一个事务是一个不可分割的工作单位,事务中的所有操作都执行成功,整个事务才算成功;只要有任何一个sql语句执行失败,那么就会回退到执行事务前的状态。
一致性:事务前后数据的完整性必须保持一致,例如银行转账,甲方转给乙方钱,不管事务成功还是失败,应该保证事务结束后,两者的存款总额为2000元。
隔离性:指的是在并发环境中,当不同的事务同时操作同一个数据时,每个事务都有各自的完整数据空间
持久性:指的是只要事务成功结束,它对数据库中数据的改变就是永久性的。
# django orm开启事务操作 from django.db import transaction with transaction.atomic(): # 在with代码块中执行的orm语句同属于一个事务 pass # 代码块运行结束 事务就结束了 事务相关的其他配置 你可以百度搜搜看
数据库的三大设计范式:
第一范式:要求属性具有原子性,不可以再分解;
不符合第一范式示例:
第二范式:2NF是对记录的唯一性,要求记录有唯一标识,即实体的唯一性,即不存在部分依赖。
表:学号、课程号、姓名、学分;
这个表明显说明了两个事务:学生信息, 课程信息;由于非主键字段必须依赖主键,这里学分依赖课程号,姓名依赖与学号,所以不符合二范式。
可能会存在问题:
数据冗余:
,每条记录都含有相同信息;删除异常:
删除所有学生成绩,就把课程信息全删除了;插入异常:
学生未选课,无法记录进数据库;更新异常:
调整课程学分,所有行都调整。
正确做法:
学生:Student
(学号, 姓名);
课程:Course
(课程号, 学分);
选课关系:StudentCourse
(学号, 课程号, 成绩)。
第三范式:3NF是对字段的冗余性
,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖;
表: 学号, 姓名, 年龄, 学院名称, 学院电话
因为存在依赖传递: (学号) → (学生)→(所在学院) → (学院电话) 。
可能会存在问题:
数据冗余:
有重复值;更新异常:
有重复的冗余信息,修改时需要同时修改多条记录,否则会出现数据不一致的情况 。
正确做法:
学生:(学号, 姓名, 年龄, 所在学院);
学院:(学院, 电话)。
范式化设计和反范式化设计的优缺点:
范式化
优点:
- 可以尽量的减少数据冗余,数据表更新快体积小
- 范式化的更新操作比反范式化更快
- 范式化的表通常比反范式化更小
缺点:
- 对于查询需要对多个表进行关联,导致性能降低
- 更难进行索引优化
反范式化
优点:
- 可以减少表的关联
- 可以更好的进行索引优化
缺点:
- 存在数据冗余及数据维护异常
- 对数据的修改需要更多的成本
django开启事务的方法
# django orm开启事务操作 from django.db import transaction with transaction.atomic(): # 在with代码块中执行的orm语句同属于一个事务 pass # 代码块运行结束 事务就结束了 事务相关的其他配置 你可以百度搜搜看
MTV与MVC模型
MTV django号称是MTV框架
M:models
T:templates
V:views
MVC
M:models
V:views
C:contronnar 控制器(路由分发 urls.py)
本质:MTV本质也是MVC
来源:https://www.cnblogs.com/cnhyk/p/12173537.html