Django之模型层

模型层

测试环境的搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""
当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
而是直接写一个测试脚本即可
 
脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以
"""
# 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
import os
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
    import django
    django.setup()
    # 在这个代码块的下面就可以测试django里面的单个py文件了

 

表的查询

复制代码
  # 1.all()  查询所有数据
    # 2.filter()     带有过滤条件的查询
    # 3.get()        直接拿数据对象 但是条件不存在直接报错
    # 4.first()      拿queryset里面第一个元素
    # res = models.User.objects.all().first()
    # print(res)
    # 5.last()
    # res = models.User.objects.all().last()
    # print(res)

    # 6.values()  可以指定获取的数据字段  select name,age from ...     列表套字典
    # res = models.User.objects.values('name','age')  # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]>
    # print(res)
    # 7.values_list()  列表套元祖
    # res = models.User.objects.values_list('name','age')  # <QuerySet [('jason', 18), ('egonPPP', 84)]>
    # print(res)
# 8.distinct()  去重
    # res = models.User.objects.values('name','age').distinct()
    # print(res)
    """
    去重一定要是一模一样的数据
    如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键
    
    """
    # 9.order_by()
    # res = models.User.objects.order_by('age')  # 默认升序
    # res = models.User.objects.order_by('-age')  # 降序
    #
    # print(res)
    # 10.reverse()  反转的前提是 数据已经排过序了  order_by()
    # res = models.User.objects.all()
    # res1 = models.User.objects.order_by('age').reverse()
    # print(res,res1)

    # 11.count()  统计当前数据的个数
    # res = models.User.objects.count()
    # print(res)
    # 12.exclude()  排除在外
    # res = models.User.objects.exclude(name='jason')
    # print(res)

    # 13.exists()  基本用不到因为数据本身就自带布尔值  返回的是布尔值
    # res = models.User.objects.filter(pk=10).exists()
    # print(res)
复制代码

django终端打印sql语句

  

复制代码
# 方式一

  res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]>
  print(res.query)
  queryset对象才能够点击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', }, } }
复制代码

基于双下划线的模糊查询

复制代码
# 神奇的双下划线查询
    # 1 年龄大于35岁的数据
    # res = models.User.objects.filter(age__gt=35)
    # print(res)
    # 2 年龄小于35岁的数据
    # res = models.User.objects.filter(age__lt=35)
    # print(res)
    # 大于等于 小于等于
    # res = models.User.objects.filter(age__gte=32)
    # print(res)
    # res = models.User.objects.filter(age__lte=32)
    # print(res)

    # 年龄是18 或者 32 或者40
    # res = models.User.objects.filter(age__in=[18,32,40])
    # print(res)

    # 年龄在18到40岁之间的  首尾都要
    # res = models.User.objects.filter(age__range=[18,40])
    # print(res)

    # 查询出名字里面含有s的数据  模糊查询
    # res = models.User.objects.filter(name__contains='s')
    # print(res)
    #
    # 是否区分大小写  查询出名字里面含有p的数据  区分大小写
    # res = models.User.objects.filter(name__contains='p')
    # print(res)
    # 忽略大小写
    # res = models.User.objects.filter(name__icontains='p')
    # print(res)

    # res = models.User.objects.filter(name__startswith='j')
    # res1 = models.User.objects.filter(name__endswith='j')
    #
    # print(res,res1)


    # 查询出注册时间是 2020 1月
    # res = models.User.objects.filter(register_time__month='1')
    # res = models.User.objects.filter(register_time__year='2020')
复制代码

 一对多外键的增删改查

复制代码
# 一对多外键增删改查
    #
    # 1  直接写实际字段 id
    # models.Book.objects.create(title='论语',price=899.23,publish_id=1)
    # models.Book.objects.create(title='聊斋',price=444.23,publish_id=2)
    # models.Book.objects.create(title='老子',price=333.66,publish_id=1)
    # 2  虚拟字段 对象
    # publish_obj = models.Publish.objects.filter(pk=2).first()
    # models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj)

    #
    # models.Publish.objects.filter(pk=1).delete()  # 级联删除

    # 修改
    # models.Book.objects.filter(pk=1).update(publish_id=2)
    # publish_obj = models.Publish.objects.filter(pk=1).first()
    # models.Book.objects.filter(pk=1).update(publish=publish_obj)
复制代码

多对多外键的增删改查

复制代码
# 如何给书籍添加作者?
    book_obj = models.Book.objects.filter(pk=1).first()
    # print(book_obj.authors)  # 就类似于你已经到了第三张关系表了
    # book_obj.authors.add(1)  # 书籍id为1的书籍绑定一个主键为1 的作者
    # book_obj.authors.add(2,3)

    # author_obj = models.Author.objects.filter(pk=1).first()
    # author_obj1 = models.Author.objects.filter(pk=2).first()
    # author_obj2 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.add(author_obj)
    # book_obj.authors.add(author_obj1,author_obj2)
    """
    add给第三张关系表添加数据
        括号内既可以传数字也可以传对象 并且都支持多个
    """

    #
    # book_obj.authors.remove(2)
    # book_obj.authors.remove(1,3)

    # author_obj = models.Author.objects.filter(pk=2).first()
    # author_obj1 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.remove(author_obj,author_obj1)
    """
    remove
        括号内既可以传数字也可以传对象 并且都支持多个
    """


    # 修改
    # book_obj.authors.set([1,2])  # 括号内必须给一个可迭代对象
    # book_obj.authors.set([3])  # 括号内必须给一个可迭代对象

    # author_obj = models.Author.objects.filter(pk=2).first()
    # author_obj1 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.set([author_obj,author_obj1])  # 括号内必须给一个可迭代对象

    """
    set
        括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
    """


    # 清空
    # 在第三张关系表中清空某个书籍与作者的绑定关系
    book_obj.authors.clear()
    """
    clear
        括号内不要加任何参数
    
    """
复制代码

 

多表查询

正反向的概念

  根据外键字段所在的表判断正反向,如果是有外键字段的表查询另一张表就是正方向,反之则是反方向

  正向查询使用外键字段查询,在子查询中如果查询的结果有多个需要加上.all()

  反向查询使用小写的表名查询,在子查询中如果结果有多个的话,需要加上_set.all()

子查询

  基于对象的跨表查询

复制代码
   # 1.查询书籍主键为1的出版社 正向子查询
    # book_obj = models.Book1.objects.filter(pk=1).first()
    # res = book_obj.publish
    # print(res)
    # print(res.publish_name)
    # print(res.addr)
    # 2.查询书籍主键为2的作者
    # book_obj = models.Book1.objects.filter(pk=2).first()
    # res = book_obj.author.all()
    # print(res)
    # 3.查询作者xzj的电话号码
    # author_obj = models.Author.objects.filter(author_name='xzj').first()
    # res = author_obj.detail
    # print(res.phone)
    # 4.查询出版社是克哥出版社出版的书 反向子查询
    # pub_obj = models.Publish.objects.filter(publish_name='克哥出版社').first()
    # res = pub_obj.book1_set.all()
    # print(res)
    # print(res[0])
    # print(res[1])
    # 5.查询作者是xk写过的书
    # author_obj = models.Author.objects.filter(author_name='xk').first()
    # res = author_obj.book1_set.all()
    # print(res)
    # 6.查询手机号是110的作者姓名
    # detail_obj = models.Author_detail.objects.filter(phone=110).first()
    # res = detail_obj.author
    # print(res.author_name)
复制代码

联表表查询

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

复制代码
# 1.查询xk的手机号和作者姓名
 # 联表查询 正向
    # res = models.Author.objects.filter(author_name='xk').values('detail__phone', 'author_name')
    # print(res)
    # 反向
    # res = models.Author_detail.objects.filter(author__author_name='xk').values('phone', 'author__author_name')
    # print(res)

 # 2.查询书籍主键为1的出版社名称和书的名称
 # 联表查询 正向
    # res = models.Book1.objects.filter(pk=1).values('title', 'publish__publish_name')
    # print(res)
    # 反向
    # res = models.Publish.objects.filter(book1__id=1).values('publish_name', 'book1__title')
    # print(res)

 # 3.查询书籍主键为1的作者姓名
 # 联表查询 正向
    # res = models.Book1.objects.filter(pk=1).values('author__author_name')
    # print(res)
    # 反向
    # res = models.Author.objects.filter(book1__id=1).values('author_name')
    # print(res)

 # 查询书籍主键是1的作者的手机号
    # 联表查询 正向
    # res = models.Book1.objects.filter(pk=1).values('author__detail__phone')
    # print(res)
    # 逆向
    # res = models.Author_detail.objects.filter(author__book1__id=1).values('phone')
    # print(res)
复制代码

聚合查询

复制代码
# 聚合查询      aggregate
    """
    聚合查询通常情况下都是配合分组一起使用的
    只要是跟数据库相关的模块 
        基本上都在django.db.models里面
        如果上述没有那么应该在django.db里面
    """
    from app01 import models
    from django.db.models import Max,Min,Sum,Count,Avg
    # 1 所有书的平均价格
    # res = models.Book.objects.aggregate(Avg('price'))
    # print(res)
    # 2.上述方法一次性使用
    res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
    print(res)
复制代码

分组查询

复制代码
 # 分组关键字 annotate
# models后面点什么 就是按什么分组

#
1.统计每一本书的作者个数 from django.db.models import Avg, Count, Sum, Max, Min # res = models.Book1.objects.annotate(author_num=Count('author')).values('title', 'author_num') # print(res) # for i in res: # print(i['author_num'])
# sql 语句 # SELECT book1_id,count(author_id) from app01_book1_author GROUP BY book1_id;

# 2.统计每个出版社卖的最便宜的书的价格 # res = models.Publish.objects.annotate(cheap_book=Min('book1__price')).values('book1__title', 'cheap_book') # print(res)
# sql 语句 # SELECT t1.publish_id,MIN(price) FROM (SELECT publish_id,price from app01_book1 INNER JOIN app01_publish on app01_book1.publish_id=app01_publish.id)as t1 GROUP BY t1.publish_id;
# 3.统计不止一个作者的图书 # res = models.Book1.objects.annotate(author_num=Count('author__id')).filter(author_num__gt=1).values('title','author_num') # print(res)
# sql 语句 # SELECT book1_id from app01_book1_author GROUP BY book1_id HAVING count(author_id) > 1;
# 4.查询每个作者出的书的总价格 # res = models.Author.objects.annotate(sum_price=Sum('book1__price')).values('sum_price', 'author_name') # print(res)

# sql语句 # SELECT t2.author_name,sum(t2.price) from (SELECT app01_author.author_name,t1.price from app01_author INNER JOIN (SELECT app01_book1.price,app01_book1_author.author_id from app01_book1 INNER JOIN app01_book1_author on app01_book1.id=app01_book1_author.book1_id) as t1 ON app01_author.id = t1.author_id) as t2 GROUP BY t2.author_name;
复制代码

F查询

复制代码
# F查询
    # 1.查询卖出数大于库存数的书籍
    # F查询
    """
    能够帮助你直接获取到表中某个字段对应的数据
    """
    from django.db.models import F
    # res = models.Book.objects.filter(maichu__gt=F('kucun'))
    # print(res)


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


    # 3.将所有书的名称后面加上爆款两个字
    """
    在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
    """
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
    # models.Book.objects.update(title=F('title') + '爆款')  # 所有的名称会全部变成空白
复制代码

Q查询

复制代码
 # Q查询
    # 1.查询卖出数大于100或者价格小于600的书籍
    # res = models.Book.objects.filter(maichu__gt=100,price__lt=600)
    """filter括号内多个参数是and关系"""
    from django.db.models import Q
    # res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600))  # Q包裹逗号分割 还是and关系
    # res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600))  # | or关系
    # res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600))  # ~ not关系
    # print(res)  # <QuerySet []>

    # Q的高阶用法  能够将查询条件的左边也变成字符串的形式
    q = Q()
    q.connector = 'or'# 查询方式修改为or,默认为and关系
    q.children.append(('maichu__gt',100))
    q.children.append(('price__lt',600))
    res = models.Book.objects.filter(q)  
    print(res)
复制代码

Djiango中开启事务

复制代码
  from django.db import transaction
    try:
        with transaction.atomic():
            # sql1
            # sql2
            ...
            # 在with代码快内书写的所有orm操作都是属于同一个事务
    except Exception as e:
        print(e)
    print('执行其他操作')
复制代码

ORM中常用字段及参数

复制代码
AutoField
    主键字段 primary_key=True
  
CharField                varchar
    verbose_name    字段的注释
  max_length        长度
  
IntegerField            int
BigIntegerField        bigint

DecimalField
    max_digits=8
  decimal_places=2

EmailFiled                varchar(254)

DateField                    date
DateTimeField            datetime
    auto_now:每次修改数据的时候都会自动更新当前时间
  auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
    
BooleanField(Field)                - 布尔值类型
    该字段传布尔值(False/True)     数据库里面存0/1

TextField(Field)                    - 文本类型
    该字段可以用来存大段内容(文章、博客...)  没有字数限制
  后面的bbs作业 文章字段用的就是TextField


FileField(Field)                    - 字符类型
   upload_to = "/data"
  给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
  /data/a.txt
  后面bbs作业也会涉及

# 更多字段
直接参考博客:https://www.cnblogs.com/Dominic-Ji/p/9203990.html

    
# django除了给你提供了很多字段类型之外 还支持你自定义字段
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):
        """
        返回真正的数据类型及各种约束条件
        :param connection:
        :return:
        """
        return 'char(%s)'%self.max_length

# 自定义字段使用
myfield = MyCharField(max_length=16,null=True)



# 外键字段及参数
unique=True
    ForeignKey(unique=True)   ===            OneToOneField()
  # 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用
  
db_index
    如果db_index=True 则代表着为此字段设置索引


to_field
    设置要关联的表的字段  默认不写关联的就是另外一张的主键字段

on_delete
    当删除关联表中的数据时,当前表与其关联的行的行为。
  """
  django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除
  """
复制代码

数据库查询优化

复制代码
only与defer    
select_related与prefetch_related

"""
orm语句的特点:
    惰性查询
        如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数
        那么orm会自动识别 直接不执行
"""
# only与defer
# res = models.Book.objects.all()
    # print(res)  # 要用数据了才会走数据库

    # 想要获取书籍表中所有数的名字
    # res = models.Book.objects.values('title')
    # for d in res:
    #     print(d.get('title'))
    # 你给我实现获取到的是一个数据对象 然后点title就能够拿到书名 并且没有其他字段
    # res = models.Book.objects.only('title')
    # res = models.Book.objects.all()
    # print(res)  # <QuerySet [<Book: 三国演义爆款>, <Book: 红楼梦爆款>, <Book: 论语爆款>, <Book: 聊斋爆款>, <Book: 老子爆款>]>
    # for i in res:
        # print(i.title)  # 点击only括号内的字段 不会走数据库
        # print(i.price)  # 点击only括号内没有的字段 会重新走数据库查询而all不需要走了

    res = models.Book.objects.defer('title')  # 对象除了没有title属性之外其他的都有
    for i in res:
        print(i.price)
    """
    defer与only刚好相反
        defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据
        而如果查询的是非括号内的字段 则不需要走数据库了

    """
    

# select_related与prefetch_related
# select_related与prefetch_related  跟跨表操作有关
    # res = models.Book.objects.all()
    # for i in res:
    #     print(i.publish.name)  # 每循环一次就要走一次数据库查询

    # res = models.Book.objects.select_related('authors')  # INNER JOIN
    """
    select_related内部直接先将book与publish连起来 然后一次性将大表里面的所有数据
    全部封装给查询出来的对象
        这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了
    
    select_related括号内只能放外键字段    一对多 一对一
        多对多也不行
    
    """
    # for i in res:
    #     print(i.publish.name)  # 每循环一次就要走一次数据库查询

    res = models.Book.objects.prefetch_related('publish')  # 子查询
    """
    prefetch_related该方法内部其实就是子查询
        将子查询查询出来的所有结果也给你封装到对象中
        给你的感觉好像也是一次性搞定的
    """
    for i in res:
        print(i.publish.name)
复制代码

 choice参数

复制代码
class User(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    # 性别
    gender_choices = (
        (1,''),
        (2,''),
        (3,'其他'),
    )
    gender = models.IntegerField(choices=gender_choices)
    
    score_choices = (
        ('A','优秀'),
        ('B','良好'),
        ('C','及格'),
        ('D','不合格'),
    )
    # 保证字段类型跟列举出来的元祖第一个数据类型一致即可
    score = models.CharField(choices=score_choices,null=True)
    """
    该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内
    那么可以非常轻松的获取到数字对应的真正的内容
    
    1.gender字段存的数字不在上述元祖列举的范围内容
    2.如果在 如何获取对应的中文信息
    """
    
      
    from app01 import models
    # 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)
    # 只要是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
    
复制代码

多对多三种创建方式

复制代码
# 全自动:利用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提供的简单的方法
  不建议你用该方式
  '''

# 半自动
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')
    author = models.ForeignKey(to='Author')

"""
through_fields字段先后顺序
    判断的本质:
        通过第三张表查询对应的表 需要用到哪个字段就把哪个字段放前面
    你也可以简化判断
        当前表是谁 就把对应的关联字段放前面
        
        
半自动:可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法
"""

# 总结:你需要掌握的是全自动和半自动 为了扩展性更高 一般我们都会采用半自动(写代码要给自己留一条后路)
复制代码

 

posted @   椰子皮0oo0  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
1
点击右上角即可分享
微信分享提示