嘘~ 正在从服务器偷取页面 . . .

DJango 学习(11)—— 模型层介绍


模型层

单表及测试环境准备

# django 自带的 sqlite3 数据库对日期格式不是很敏感 处理的时候容易出错

测试脚本

"""
当你只是想要测试 django 中的一个 py 文件时 可以不用书写前后端交互的形式,而是直接写一个测试脚本
"""
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoProject.settings")
    
    import django
    django.setup()
    
    from app01 import models

查看内部 sql 语句

# 方式一
queryset 对象才能点击 query 查询内部的 sql 语句
res = models.USER605.objects.values_list("name")
print(res.query)

# 方式二:所有的 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',
        },
    }
}

必知必会13条

    # 1. all    查询所有

    # 2. filter   带有过滤条件的查询,

    # 3. get   直接拿数据对象,但不存在时会报错

    # 4. first    拿 queryset 里面的第一个对象

    # 5. last   拿 queryset 里面的最后一个对象

    # 6. values   可以指定获取的数据字段    select name, age from ....    返回结果是列表套字典
        # res = models.USER605.objects.values("name")
        # res1 = models.USER605.objects.values("name", "age")
        # print(res)
        # <QuerySet [{'name': 'aoteman'}, {'name': 'alterman'}]>

    # 7. values_list    结果是(类似于)  列表套元组
    #     res = models.USER605.objects.values_list("name")
    #     print(res)
        # print(res.query)
        # <QuerySet [('aoteman',), ('alterman',)]>

    # 8. distinct   去重,一定要不包含主键
        # res = models.USER605.objects.all().distinct()
        # print(res)
        #
        # res = models.USER605.objects.values("name").distinct()
        # print(res)

    # 9. order_by  排序
    #     res = models.USER605.objects.order_by("age")
    #     print(res.values("name")) # 默认升序
    #
    #     res = models.USER605.objects.order_by("-age") # 加 “-” 降序

    # 10. reverse  反转,前提是已经排序了
        # res = models.USER605.objects.order_by("age").reverse()
        # print(res.values("name"))


    # 11. count 统计当前数据的格式
        # res = models.USER605.objects.count()
        # print(res)

    # 12. exclude    排除 什么什么 在外
        # res = models.USER605.objects.exclude(name="ash")
        # print(res.values())

    # 13. exists   判断是否存在,返回 布尔值
        # res = models.USER605.objects.filter(pk=4).exists()
        # print(res)

神奇的双下划线查询

    # 1. 年龄大于30的数据
    # res = models.USER605.objects.filter(age__gt=30)
    # print(res.values("name"))

    # 小于
    # age__lt

    # 大于等于  小于等于
    # age__gte   age__lte

    # 年龄 18 或 40 的数据
    # res = models.USER605.objects.filter(age__in=[18, 40])
    # print(res.values("name"))

    # 年龄 18 到 40 之间的  (首尾都要)
    # res = models.USER605.objects.filter(age__range=[18,40])
    # print(res.values("name"))

    # 名字内有 n 的数据   模糊查询
    # res = models.USER605.objects.filter(name__contains="n")
    # print(res.values("name"))

    # 是否区分大小写    <QuerySet []>    默认区分大小写
    # res = models.USER605.objects.filter(name__contains="N")
    # print(res.values("name"))

    # 忽略大小写   __icontains
    # res = models.USER605.objects.filter(name__icontains="N")
    # print(res.values("name"))

    # 名字以 xxx 开头的
    # res = models.USER605.objects.filter(name__startswith="a")
    # print(res.values("name"))

    # 以xxx为结尾的
    # endswith

    # 注册时间是 2002年 1 月份的数据
    # res = models.USER605.objects.filter(register_time__month="1")
    # print(res.values("name"))

外键的增删查改

一对多(一对一)外键

一对多外键
    # 增
    # 方法一:实际字段 id
    models.book606.objects.create(title="三国演义", price=123.12, publish_id=1)
    # 方法二:虚拟字段 对象
    pushlish_obj = models.publish606.objects.filter(pk=1).first()
    models.book606.objects.create(title="奥特曼", price=111.11, publish=pushlish_obj)

    # 删,级联更新,级联删除
    models.publish606.objects.filter(pk=1).delete()

    # 修改数据
    # 和增加数据一样有两种方法
    models.book606.objects.filter(pk=1).update(publish_id=2)
    obj = models.publish606.objects.filter(pk=2).first()
    models.book606.objects.filter(pk=1).update(publish=obj)

多对多外键

多对多  增删改查    就是在操作第三张表
    # 如何给书籍绑定作者?
    # add 给第三张表添加数据——括号内可以传数字也可以传对象,并且都支持多个
    author_obj = models.author606.objects.filter(pk=2).first()
    print(author_obj.author2book)
    author_obj.author2book.add(1) # 给 id 为 1 的作者绑定一个主键为 1 的书籍
    author_obj.author2book.add(2, 3) # 多个参数

    # 参数也支持对象
    book_obj = models.book606.objects.filter(pk=1).first()
    book_obj1 = models.book606.objects.filter(pk=2).first()
    book_obj2 = models.book606.objects.filter(pk=3).first()

    author_obj.author2book.add(book_obj, book_obj1, book_obj2)


    # 删  remove 删除数据 可以传入数字也可以传入对象
    author_obj = models.author606.objects.filter(pk=1).first()

    author_obj.author2book.remove(2, 3)

    # 修改
    # set 传入的是列表(可迭代对象),列表元素既可以是数字,也可以是对象,本质是先删除后添加
    author_obj = models.author606.objects.filter(pk=2).first()

    author_obj.author2book.set([1, 3]) # 括号内应该是可迭代对象

    # 清空
    # 在第三张表中清空某个书籍与作者的绑定关系
    clear 内部什么都不要传入
    author_obj = models.author606.objects.filter(pk=2).first()
    author_obj.author2book.clear()

正反向的概念

# 正向
    看外键字段在哪里。
    外键字段在A,A查B 就是正向
    外键字段在B,A查B 就是反向
# 反向

'''
正向查询按字段
反向查询按表名小写
        _set
        ...
'''

多表查询

子查询(基于对象的跨表查询)

基于对象的跨表查询

    #查询书籍主键为 1 的出版社名称
    book_obj = models.book606.objects.filter(pk=1).first()
    # 书查出版社按字段
    res = book_obj.publish
    print(res.name)
    print(res.addr)

    # 查询作者主键为 1 的书籍
    author_obj = models.author606.objects.filter(pk=1).first()
    # 作者查书 正向
    res = author_obj.author2book.all() # <QuerySet [<book606: book606 object>]>
    print(res)

    # 查询 作者 jason 的电话号码
    author_obj = models.author606.objects.filter(name="jason").first()
    res = author_obj.authordetail
    print(res.phone)

    # 什么时候加 .all 方法——当结果有多个时

    # 查询出版社是东方出版社的书   反向
    publish_obj = models.publish606.objects.filter(pk=1).first()
    res = publish_obj.book606_set.all()
    print(res.values("title"))

    # 查询聊斋的作者
    book_obj = models.book606.objects.filter(pk=4).first()
    res = book_obj.author606_set.all()
    print(res.values("name"))

    # 查询手机号是 110 的作者姓名
    author_detail_obj = models.authordetail606.objects.filter(pk=1).first()
    res = author_detail_obj.author606
    print(res.name)

    # 当你的查询结果有多个时需要    _set.all()

联表查询(基于双下划线的跨表查询)

基于双下划线的跨表查询

    # 1. 查询 jason 的手机号
    res = models.author606.objects.filter(name="jason").values("authordetail__phone")
    print(res)

    # 2. 查询书籍主键为 1 的出版社名称和图书的名字
    res = models.book606.objects.filter(pk=1).values("title","publish__name", "publish__addr")
    print(res)

    # 3. 查询作者主键为 1 的书籍名称
    res = models.author606.objects.filter(pk=1).values("author2book__title")
    print(res)

    # 查询 jason 的手机号(不使用 .author606)    反向
    res = models.authordetail606.objects.filter(author606__name="jason") # 拿作者是 jason 的字段
    res = models.authordetail606.objects.filter(author606__name="jason").values("phone","author606__name")
    print(res)

    # 反向2
    res = models.publish606.objects.filter(book606__id=1).values("book606__title", "addr")
    print(res)

    # 反向3
    res = models.book606.objects.filter(author606__id=1).values("title", "author606__name")
    print(res)


    # 查询书籍主键是 1 的作者的手机号
    res = models.book606.objects.filter(pk=1).values("author606__authordetail__phone")
    print(res)

    # 只要掌握了正反向的概念和双下划线跨表查询就可以无限跨表

聚合查询

# 聚合查询,通常情况下都是配合分组一起使用   aggregate
    # 只要是和数据库相关的模块基本上都在 django.db.models里面
    # 或者是 django.db 里面
    from django.db.models import Sum,Count,Min,Max,Avg

    # 统计书的平均价格
    res = models.book606.objects.aggregate(Avg("price"))
    print(res)

    # 上述方法可以一起使用

分组查询

   # 分组查询 annotate

    # 统计每本书的作者数量
    # res = models.Book.objects.annotate()  # models后面点什么 就是按什么分组
    from django.db.models import Sum, Count, Min, Max, Avg
    # author_num 是我们自己定义的字段,用来存储统计出来的数据
    res = models.book606.objects.annotate(author_num=Count('author606')).values("title","author_num")
    print(res)

    # 统计每个出版社卖的最便宜的书的价格
    res = models.publish606.objects.annotate(book_price=(Min('book606__price'))).values("name", "book_price")
    print(res)

    # 统计不止一个作者的图书
        # 先按图书分组,求每一本书对应的作者个数
        # 过滤出不止一个作者的图书
    res = models.book606.objects.annotate(author_num=Count("author606")).filter(author_num__gte=2).values("title")
    print(res)

    # 只要你的 orm 语句得出的结果是一个 queryset 对象,那么它就可以无限制的点 queryset 对象所封装的方法


    # 查询每个作者出的书的总价格
    res = models.author606.objects.annotate(total_price=Sum("author2book__price")).values("name", "total_price")
    print(res)

    # 按某个字段分组    以价格为例
    models.book606.objects.values("price").annotate()

    # 如果分组查询报错,需要关闭严格模式

F查询

    from django.db.models import F


    # 查询 卖出大于库存 的数据
    res = models.book606.objects.filter(maichu__gt=F("kucun")).values("title")
    print(res)

    # 将所有书籍的价格提升 500 块
    models.book606.objects.update(price=F("price")+500)

    # 将所有书的名称后面加上  爆款  两个字
    # 在操作字符数据类型的时候 F不能够直接做到字符串的拼接

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

Q查询

    from django.db.models import Q
    
    # 查询 卖出数大于100 或者 价格小于600 的书籍

    res = models.book606.objects.filter(Q(price__lt=600), Q(maichu__gt=100)).values("title") # Q 包裹 逗号 还是and 关系

    # Q查询可以使用 |。| 表示 或; ~ 表示 取反
    res = models.book606.objects.filter(Q(price__lt=600)|Q(maichu__gt=100)).values("title")
    print(res)


    # Q 的高阶用法   能够将 查询条件的左边也变成字符串的形式
    q = Q()
    # q.connector = "or" # 修改为 or
    q.children.append(("maichu__gt", 100))
    q.children.append(("price__lt", 600))
    res = models.book606.objects.filter(q).values("title") # 默认还是 and 关系
    print(res)

django中开启事务

"""
事务
    ACID
        原子性
            不可分割的最小单位
        一致性
            跟原子性相辅相成
        隔离性
            事务之间互相不干扰
        持久性
            事务一旦确认永久生效
    事务的回滚
        rollback
    事务的确认
        commit
"""
# 目前只需要掌握如何简单的开启事务
# 事务
    from django.db import transaction
    with transaction.atomic():
        # sql1
        # sql2
        # 在 with 代码块中书写的所有 orm 操作都属于同一个事务
    print('执行其他操作')

orm 中常用字段以及参数

AutoField
    主键字段 primary_key = True
    
CharField      对应 varchar
    verbase_name   字段的注释
    max_length      长度

IntergeField       int

BigIntergeField    bigint

DecimalField
    max_disgits     总位数
    decimal_places   小数位数
    
EmailField           varcahr(254)

DateField            date
 
DateTimeField        datetime
    auto_now        修改数据时自动更新时间
    auto_now_add    创建数据时填入时间,之后就不会自动修改
    
BooleanField         布尔值类型
    该字段传入 布尔值(True/False),数据库里面存 0/1
    
TextField           文本类型
    可以存大段内容(文章、博客...)没有字数限制
    
FileField
    upload_to = "/data" 给该字段传入一个文件对象,会自动将文件保存到 /data 目录下,然后将文件路径存到数据库中
    
# 更多字段:https://www.cnblogs.com/Dominic-Ji/p/9203990.html


# 还支持自定义字段
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):
        # 返回数据类型及约束条件
        return "char(%s)" % self.max_length
    
    

# 外键字段以及参数
unique = True
    ForeignKey(unique=True) === OneToOneField()
    # 在用前面字段创建 一对一 orm会有一个提示信息 orm 推荐使用后者
    
to_field
    设置要关联的表的字段  默认不写就是关联主键

on_delete
    级联删除,当删除关联表中的数据时,当前表与其关联的行的行为。

数据库查询优化

only 与 defer

select_related 与 prefetch_related

"""
orm 语句的特点
    惰性查询
        如果仅仅书写 orm 语句,后面没有用到查询的数据,该orm语句不会执行
"""

# only 与 defer
    # 获取书籍表中所有书籍的名字
    res = models.book606.objects.values("title")
    for i in res:
        print(i.get("title"))
    # 获取到的时一个数据对象 然后点title就能够拿到书名 并且没有其他字段
    res = models.book606.objects.only("title")
    for i in res:
        print(i.title) # 点击 only 括号内的字段,不会走数据库
        print(i.price) # 点击 only 括号内以外的字段,会重新走数据库查询,而 all 不需要走

    # defer 和 only 刚好相反
    res = models.book606.objects.defer("title") # 对象除了没有 title 对象其他字段都有
    for i in res:
        print(i.price)
        
        
# select_related 与 prefetch_related   跟跨表操作有关
    res = models.book606.objects.all()
    for i in res: # 每循环一次需要走一次数据库查询
        print(i.publish.name)

    res = models.book606.objects.select_related("publish") # inner join
    # select_related 内部先联表,一次性将大表数据全部封装给查询出来的对象,这个时候对象无论什么时点击两张表的任何数据都不需要再次查询了
    # select_related 括号内只能放 外键字段  一对一  一对多
    # 多对多也不行
    for i in res:
        print(i.publish.name)


    res = models.book606.objects.prefetch_related("publish") # 该方法内部是子查询,将子查询的所有结果都封装到对象中,给人的感觉像是一次性操作
    for i in res:
        print(i.publish.name)

文章作者: New Ass
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 New Ass !
  目录