Django模型层

模型层与ORM

一、初步认识ORM

1.什么是ORM

对象关系映射  能够让不会SQL语句的python程序员 使用python面向对象的语法来操作数据库  优势>>>:简单方便快捷 劣势>>>:效率可能会低 

2.概念对接

类 		   表 对象 		   一条条数据 对象点名字	 数据获取字段对应值 

3.基本操作(增删改查)

models.UserInfo.objects.create()  # insert into 增 models.UserInfo.objects.filter().delete()  # delete from 删 models.UserInfo.objects.filter().update()  # update 改 models.UserInfo.objects.filter()  # where 查 
from app01 import models   class Info(models.Model):     # 字段名 = 字段类型 + 约束条件     id = models.AutoField(primary_key=True)     name = models.CharField(max_length=32)     age = models.IntergerField() 

4.数据库迁移命令

python38 manage.py makemigrations  # 将操作记录到小本本上 python38 manage.py migirate  # 将操作同步到数据库上 '''每次在models.py中与数据相关的代码 都需要执行数据库迁移命令''' 

二、ORM常用关键字

# 关键概览 1.create() 2.filter() 3.first() last() 4.update() 5.delete() 6.all() 7.values() 8.values_list() 9.distinct() 10.order_by() 11.get() 12.exclude() 13.reverse() 14.count() 15.exists() 
# 1.create()  创建数据并直接当前创建的数据对象 res = models.UserInfo.objects.create(name='almra', age=24) 
# 2.filter() 根据条件筛选数据 结果是QuerySet [数据对象1,数据对象2] res = models.UserInfo.objects.filter(name='amira')  # 括号没有写默认全部筛选 括号内支持多个条件 默认是and 
# 3.first() last()  QuerySet支持索引取值但只是支持正数 并且orm不建议使用索引 res = models.UserInfo.objencts.filter()[1]  # 查询到列表对应的数据 对应数据不存在会报错 res = models.UserInfo.objects.filter().first()  # 查询到列表第一个数据 对应数据不存在不会报错返回None 
# 4.update()  更新数据 res = models.UserInfo.objects.filetr().update()  # 批量更新 res = models.UserInfo.objects.filetr(id=1).update()  # 自定义更新 
# 5.delete()  删除数据(批量删除) models.UserInfo.objects.filter().delete()  # 批量删除 models.UserInfo.objects.filter(id=1).delete()  # 单个删除 
# 6.all()  查询所有数据 结果是QuerySet [数据对象1,数据对象2] res = models.UserInfo.objects.all() 
# 7.values()  根据指定字段获取数据    结果是QuerySet [{},{},{},{}] res = models.UserInfo.objects.all().values('name') res = models.UserInfo.objects.filter().values() res = models.UserInfo.objects.values() 
# 8.values_list()  根据指定字段获取数据 结果是QuerySet [(),(),(),()] res = models.UserInfo.objects.all().values_list('name','age') 
# 9.distinct()   去重 数据一定要一模一样才可以 如果有主键肯定不行 res = models.UserInfo.objects.values('name','age').distinct() 
# 10.order_by()  根据指定条件排序 默认是升序 字段前面加负号就是降序 res = models.UserInfo.objects.all().order_by('age') 
# 11.get()   根据条件筛选数据并直接获取到数据对象  一旦条件不存在会直接报错 不建议使用 res = models.UserInfo.objects.get(pk=1) 
# 12.exclude()  取反操作 res = models.UserInfo.objects.exclude(pk=1) 
# 13.reverse()  颠倒顺序(被操作的对象必须是已经排过序的才可以) res1 = models.UserInfo.objects.all().order_by('age').reverse() 
# 14.count()  统计结果集中数据的个数 res = models.User.objects.all().count() 
# 15.exists()  判断结果集中是否含有数据 如果有则返回True 没有则返回False res = models.User.objects.all().exists() 

三、ORM查询SQL语句的方法

方式1:raw()

models.User.objects.raw('select * from app01_user;') 

方式2:cursor()

from django.db import connection cursor = connection.cursor() cursor.execute('select name from app01_user;') print(cursor.fetchall()) 

四、神奇的双下划线查询

只要还是queryset对象就可以无限制的点queryset对象的方法 比如:queryset.filter().values().values_list().filter().... 

1.查询年龄大于18岁的用户数据/年龄小于38岁的用户数据

res = models.Users.objects.filter(age__gt=18)  # 大于18岁 res = models.Users.objects.filter(age__lt=38)  # 小于18岁 

2.查询年龄大于等于18岁的用户数据/年龄小于等于38岁的用户数据

res = models.Users.objects.filter(age__gte=18)  # 大于等于18岁 res = models.Users.objects.filter(age__lte=38)  # 小于等于18岁 

3.查询年龄是18岁或28岁或38岁的用户数据

res = models.User.objects.filter(age__in=(18, 28, 38)) 

4.查询年龄在18岁到38岁之间所有用户数据

res = models.User.objects.filter(age__range=(18, 38)) 

5.查询名名字中含有字母j的用户数据

res = models.User.objects.filter(name__contains='j')  # 只查j不查J字母 res = models.User.objects.filter(name__incontains='j')  # J,j都会查出来 

6.查询注册年份是2022年的用户数据

res = models.User.objects.filter(register_time__year=2022) 

五、ORM外键字段创建

1.ORM外键字段创建理论铺垫

MySQL外键关系 一对多:外键字段在多的一方 多对多:外键字段统一建在第三张关系表 一对一:建议放在查询频率较高的表 
ORM确定外键关系 一对多:外键字段在多的一方 跟MySQL一致 多对多:外键字段建在查询频率较高的表(内部自动创建第三张表)  一对一:建议放在查询频率较高的表 跟MySQL一致 
ORM创建外键字段代码语句 针对一对多和一对一同步到表中之后会自动加_id的后缀 publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE) author_detail = models = models.OneToOneFiled(to='AuthorDetail', on_delete=models.CASCADE) 针对多对多不会在表中展示而是创建第三张表 authers = models.ManyToManyField(to='Author') 

2.ORM外键字段创建相关操作

# 针对一对多 插入数据可以直接填写表中的实际字段 models.Book.objects.create(title='编程基础教学', price=888.88, publish_id=1) models.Book.objects.create(title='编程进阶教学', price=999.99, publish_id=1) # 针对一对多 插入数据也可以填写表中的类中字段 publish_obj = models.Publish.objects.filter(pk=1).first() models.Book.objects.create(title='高级编程指导书', price=1000, publish=publish_obj)  
'''一对一与一对多 一致''' 既可以传数字也可以传对象 # 针对多对多关系绑定 book_obj = models.Book.objects.filter(pk=1).first() book_obj.authors.add(1)  # 在第三张关系表中给当前书籍绑定作者 book_obj.authors.add(2, 3) book_obj = models.Book.objects.filter(pk=4).first() author_obj1 = models.Author.objects.filter(pk=1).first() author_obj2 = models.Author.objects.filter(pk=2).first() book_obj.authors.add(author_obj1) book_obj.authors.add(author_obj1, author_obj2) book_obj = models.Book.objects.filter(pk=1).first() 
book_obj.authors.set((1, 3))  # 修改关系 book_obj.authors.set([2, ])  # 修改关系 author_obj1 = models.Author.objects.filter(pk=1).first() author_obj2 = models.Author.objects.filter(pk=2).first() book_obj.authors.set((author_obj1,)) book_obj.authors.set((author_obj1, author_obj2)) book_obj.authors.remove(2) book_obj.authors.remove(1, 3) book_obj.authors.remove(author_obj1,) book_obj.authors.remove(author_obj1,author_obj2) book_obj.authors.clear() 

二、多对多三种创建方式

1.全自动创建 2.纯手动创建 3.半自动创建 
1.全自动创建 class Book(models.Model):     title = models.CharField(max_length=32)     authors = models.ManyToManyField(to='Authors', on_delete=models.CASCADE) class Authors(models.Model):     name = models.CharField(max_length=32)      # 优势:自动创建第三张表 并提供了add、remove、set、clear四种操作 # 劣势:第三张表无法创建更多的字段 扩展性比较差 
2.手动创建 class Book(models.Moldel):     title = model.CharField(max_length=32) class Author(models.Model):     name = moldel.CharField(max_length=32) class Book2Author(models.Model):     book = models.ForeignKey(to='Book',on_delete=models.CASCADE)     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)     author = models.ForeignKey(to='Author', on_delete=models.CASCADE)     others = models.CharField(max_length=32)     join_time = models.DateField(auto_now_add=True)      # 优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用 # 劣势:编写繁琐不再支持add、remove、set、clear 
# 总结 add()remove()	     多个位置参数(数字 对象) set()			    可迭代对象(元组 列表) 数字 对象  clear()			    情况当前数据对象的关系 

六、多表查询(基于对象和双下划线)

1.ORM跨表查询理论

MySQL跨表查询 子查询:分布操作(将一条SQL语句用括号括起来当作另外一条SQL语句的条件) 连表操作:先整合多张表之后基于单表查询即可('inner'/left/right/union join) 
正反向查询(重要) 正向:由外键字段所在的表数据查询关联的表查询 反向:无外键字段的表数据查询关联的表数据查询 # 技巧:核心就看外键字段在不在当前数据所在的表中 
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 print(book_obj.authors.all()) print(book_obj.authors.all().values('name')) 
# 3.查询almira的电话号码 author_obj = models.Author.objects.filter(name='almira').first() print(author_obj.author_detail.phone) 
# 4.查询北方出版社出版过的书籍 publish_obj = models.Publish.objects.filter(name='新疆维吾尔出版社').first() print(publish_obj.book_set)  # app01.Book.None print(publish_obj.book_set.all()) 
# 5.查询almira写过的书籍 author_obj = models.Author.objects.filter(name='almira').first() print(author_obj.book_set)  # app01.Book.None print(author_obj.book_set.all()) 
# 6.查询电话号码是18988998899的作者姓名 author_detail_obj = models.AuthorDetail.objects.filter(phone=18988998899).first() print(author_detail_obj.author) print(author_detail_obj.author.name) 

3.基于双下划线的跨表查询

 # 1.查询主键为1的书籍对应的出版社名称 res = models.Book.objects.filter(pk=1).values('publish__name','title') print(res) 
# 2.查询主键为4的书籍对应的作者姓名 res = models.Book.objects.filter(pk=4).values('title', 'authors__name') print(res) 
 # 3.查询almira的电话号码 res = models.Author.objects.filter(name='almira').values('author_detail__phone') print(res) 
# 4.查询北方出版社出版过的书籍名称和价格 res = models.Publish.objects.filter(name='新疆维吾尔出版社').values('book__title','book__price','name') print(res) 
# 5.查询almira写过的书籍名称 res = models.Author.objects.filter(name='almira').values('book__title', 'name') print(res) 
# 6.查询电话号码是110的作者姓名 res = models.AuthorDetail.objects.filter(phone=110).values('phone', 'author__name') 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.查询almira的电话号码 res = models.AuthorDetail.objects.filter(author__name='almira').values('phone') print(res) 
# 4.查询北方出版社出版过的书籍名称和价格 res = models.Book.objects.filter(publish__name='新疆维吾尔出版社').values('title','price') print(res) 
# 5.查询almira写过的书籍名称 res = models.Book.objects.filter(authors__name='almira').values('title') print(res) 
# 6.查询电话号码是18988998899的作者姓名 res = models.Author.objects.filter(author_detail__phone=18988998899).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) 

七、聚合查询

聚合函数:Max Min Sun Count Avg 在ORM中支持单独使用聚合函数 用关键字:aggregate      from django.db.models import Max, Min, Sum, Avg, Count res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk')) print(res) 

八、分组查询

# 温馨提示来袭哟! 如果执行ORM分组查询报错 并且有关键字sql_mode strict mode  移除sql_mode中的only_full_group_by 即可! 代码如下  set global sql_mode='stric_trans_tables'; 

1.统计每一本书的作者个数

res = models.Book.objects.annotate(author_num=Count('authors_pk')).values('title', 'author_num') print(res) 

2.统计出每个出版社卖出的最便宜的书的价格

res = models.Publish.objects.annotate(min_price=Min('book_price')).values('name', 'min_price') print(res) 

3.统计不止一个作者的书

# 1.先统计每本书作者个数 res = models.Book.objects.annotate(author_num=Count('author_pk')) # 2.再从中筛选作者个数大于1的数据 res.filter(author_num_gt=1).values('title', 'author_num') 

3.查询每个作者的书总价格

res = models.Author.objects.annotate(totalprice=Sum('book__price'),count_book=Count('book_pk')).values('name','totalprice','count_book') print(res) 
# 按照表名分组 models.表名.objects.annotate() # 按照values括号内指定的字段分组 models.表名.objects.values('字段名').annotate() 
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id','count_pk') print(res) 

九、F与Q查询

1.什么是F查询? F查询:同一张表格的不同字段之间的查询 当查询条件不是明确的 也需要从数据库中获取 就需要使用F查询 
2.什么是Q查询? Q查询:需要复杂的逻辑关系的时候使用Q查询 逗号默认是and,处理不了or not 这样的更复杂查询条件 这时Q查询用上排场了 
| 表示取或or & 表示取且and ~ 表示取反not 

1.查询库存数量大于卖出的书籍数量

from django.db.models import F res = models.Book.objects.filter(stock__gt=F('sold')) print(res) 

2.将所有书的价格涨800

from django.db.models import F models.Book.objects.update(price=F(price) + 800) 

3.将所有书的名称后面追加爆款

from django.db.models import F from django.db.models.functions import Concat from django.db.models import Value  models.Book.objects.update(title=Concat(F('title'),Value('爆款'))) 

4.查询主键是1或者大于2000的书籍

from django.db.models import Q res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000)) 

十、Q查询进阶操作

from django.db.models import Q q_obj = Q()  # 1.产生Q对象 q_obj.connector = 'or'  # 默认多个条件的连接是and可以修改为or q_obj.children.append(('pk', 1))  # 2.添加查询条件 q_obj.children.append(('price__gt', 2000))  # 支持添加多个 res = models.Book.objects.filter(q_obj)  # 查询支持直接填写q对象 print(res) 

十一、ORM查询优化

1.ORM的查询默认都是惰性查询

如果你仅仅只是书写了ORM语句  没有用该语句所查询出来的参数 那么ORM会自动识别 直接不执行 这个性质可以提升效率 节省资源     

2.ORM自带分页查询(limit)

平时我们学习过程当中处理数据的量很少 我们没有注意到ORM自带的分页查询功能 将来在工作过程中要处理的数据上千万的  这个时候我们得关注一下ORM的分页查询  如果没有分页查询功能那数据库内存也会  受不了的 直接回导致项目瘫痪 系统崩掉    

3.only与defer

res = models.Book.objects.only('title', 'price')  # only关键字查询详解 print(res)  # 结果是queryset对象[数据对象、数据对象] for obj in res:     print(obj.title)  # 点击括号内填写的字段 不走SQL查询语句     print(obj.publish_time)  # 可以执行括号内没有的字段并获取数据 但是会走SQL查询语句 
res = models.Book.objects.defer('title', 'price')  # defer关键字查询详解 print(res)  # 结果是queryset对象[数据对象、数据对象] for obj in res:     print(obj.title)  # 点击括号内填写的字段 走SQL查询语句     print(obj.publish_time)  # 点击括号内没有的字段获取数据 不会走SQL查询语句 

4.selected_related与prefetch_related

res = models.Book.objects.selected_related('authors')  # selected_related相当于连表查询 不支持多对多 for obj in res:     print(res) 
res = models.Book.objects.prefetch_related('publish')  # prefetch_related相当于子查询 for obj in res:     print(obj.publish.name) 

十二、ORM批量操作数据(ORM操作优化)

关键字: bulk_create()  # 批量创建数据 bulk_update()  # 批量更新数据 
def ab_bk_func(request):     book_obj_list = []  # 可以用列表生成式[... for i in ... if ...]      for i in range(1, 100000):         book_obj = models.Books01(title='第%s本书' % i)  # 单纯的用类名加括号产生对象         book_obj_list.append(book_obj)     models.Books01.objects.bulk_create(book_obj_list)  # 批量插入数据     """使用orm提供的批量插入操作 5s 10万条左右"""     book_queryset = models.Books01.objects.all()  # 查询出所有的表中并展示到前端页面     return render(request, 'BkPage.html', locals()) 

十三、ORM事务操作

1.SQL事务相关理论知识

1.事务的四大特性(ACID)     A(Atomicity 原子性)     C(Consistency 一致性)     I(Isolation 隔离性)     D(Durability 持续性)   2.相关SQL关键字     start transaction;  # 启动事务     rollback;  # 回退 回滚     commit;  # 把事务保存到数据库     savepoint;  # 在回滚中保存一个节点下次直接定位到此节点  3.相关SQL重要概念     脏读:是指一个线程中的事务读取到了另外一个线程中未提交的数据     幻读 :指一个线程中的事务读取到了另外一个线程中提交的insert的数据     不可重复读: 是指一个线程中的事务读取到了另外一个线程中提交的update的数据     MVCC多版本控制:是通过数据行的多个版本管理来实现数据库的并发控制 

2.django orm 提供了三种开启事务方法

# 方法一:在配置文件里写如下代码 全局有效 'ATOMIC_REQUESTS':True 
# 方法二:装饰器 局部有效 from django.db import transation @transaction.atomic def index():pass 
# 方法三:with上下文管理  局部有效 from django.db import transation def reg():     with transaction.atomic():pass  

十四、ORM常用字段

1.AutoFiled(primary_key=True)  # 主键 2.CharField(max_length=32)  # 字符 3.IntegerField  # 整型 4.BigIntegerField  # 大整型 5.DecimalField(max_digits=8, decimal_places=2)  # 十进制字段 6.DateField(auto_now, auto_now_add)	 # 年月日 7.DateTimeField(auto_now, auto_now_add)  # 年月日时分秒 8.BooleanField  # 传布尔值自动存0或1 9.TextField  # 存储大段文本 10.EmailField  # 存储邮件格式数据 11.FileField  # 传文本对象 自动保存到提前配置好的路径下并保存路径信息 
# 可以自定义字段 class MyCharField(models.Field):     def __init__(self,max_length, *args, **kwargs):         self.max_length = max_length         super().__init__(max_length=manx_length, *args, **kwargs)              def db_type(self, connection):         return 'char(%s)' % self.max_length           class User(models.Model):     name = models.CharField(max_length=32)     info = MyCharField(max_length=64) 

十五、ORM常用字段参数

primary_key  # 主键 verbose_name  # 注释 max_length  # 字段长度 max_digits  # 小数总共多少位 decimal_places  # 小数点后面的位数 auto_now  # 每次操作数据自动更新事件 auto_now_add  # 首次创建自动更新事件 后续不会自动更新 null  # 允许字段为空 default  # 字段默认值 unique  # 唯一值 db_index  # 给字段添加索引 choices  # 当某个字段的可能性能被列举完全的情况下使用 
外键字段 to  #关联表 to_field  # 关联字段 on_delete=models.CASCADE  # 级联操作 当主表删除一条数据 从表关联数据同时被删除 on_delete=models.SET_NULL  # 当主表删除一条数据 从表关联字段设置为null 定义外键必须可以允许为空 on_delete=models.PROTECT  # 当主表删除一条数据 从表关联字段是受保护的外键 所以都不允许删除 on_delete=models.SET_DEFAULT  # 当主表删除一条数据 从表关联字段设置为默认值 定义外键必须有一个默认值 on_delete=models.SET()  # 当主表删除一条数据 从表关联字段设置为SET()中设置的值 on_delete=models.DO_NOTHING  # 什么都不做 一切都看数据库级别的约束 
# 详细说一下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() 

发表评论

相关文章