Django模型层

模型层

1、测试脚本

"""
当你只是想想测试django中的某一个 py文件内容,那么你可以不用书写前后端交互的形式,而是直接写一个测试脚本即可

脚本代码无论写在应用下的tests.py还是写在自己单独开设的py文件都可以
"""

#测试环境准备
	# 1.去manage.py中拷贝部分代码,
    #2.
        #import django
        #django.setup()

    
    
#tests.py    
import os

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06.settings')
    import django
    django.setup()
    
	#在代码块的下main就可以测试django里的单个py文件了
    from app01 import models
    ......
if __name__ == '__main__':
    main()

2、单表操作

2. 1、表准备

"""
Django自带的sqlite3对日期格式不是很敏感,处理的时候容易出错
"""
class User(models.Model):
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField()
    register_time = models.DateField(auto_now_add=True)  # 年月日
    """
    DateTime     
    DateTimeField
        两个重要参数,
                    auto_now   :每次操纵数据的时候,该字段会自动将 当前时间更新
                    auto_now_add  :在创建数据的时候,会自动将当前创建时间记录下来,之后只要不修改,那么就一直不变
                    
    """
    

2. 2、操作

#增
1.create()
2.对象.save()

#查
1.all()  #查所有
2.filter() #筛选条件,括号内多个参数之间逗号隔开,并且默认是and的关系
3.get() #筛选条件,条件不存在直接报错,不推荐使用

#改
1.update  #queryset对象帮你封装的批量更新
2.对象.save()

#删
1.delete() #queryset对象帮你封装的批量删除
2.对象.delete()

"""在实际项目中,数据是不可能真正删除的,一般情况下都用一个字段来标记是否删除"""
#tests.py

from django.test import TestCase

# Create your tests here.
import os


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06.settings')
    import django
    django.setup()

    from app01 import models
    增
    #方法一
    # models.User.objects.create(name='zhao',age=18,register_time='2002-1-21')
   
	#方法二
	# import datetime
    # ctime = datetime.datetime.now()
    # user_dic = models.User(name='lisi', age=19, register_time=ctime)
    # user_dic.save()
    
     删
    #方法一
    # res = models.User.objects.filter(pk=2).delete()
    # print(res)
    """
        pk会 自动查找到当前表的主键字段,指代的就是当前表的主键字段
        用了pk就不需要知道当前表的主键字段到底叫什么了
        
    """
	#方法二
    # user_obj=models.User.objects.filter(pk=1).first()
    # user_obj.delete()
    
    修改
    # models.User.objects.filter(pk=2).update(name='张三')
    
    #user_obj = models.User.objects.get(pk=4)
    #user_obj.name='lisi'
    #user_obj.save()
    """
    get方法返回的直接就是当前数据对象
    但是不推荐使用,
        一旦数据不存在,该方法直接报错
        而filter不会报错,返回空
    """
   

2.3、常见13种单表查询方法

all()  #查询所有数据
	 models.User.objects.all()
    
filter()  #带有过滤条件的查询
	models.User.objects.filter()
    models.User.objects.filter(pk=2)
    
get()  #直接拿数据对象,但是条件不存在会报错
	user_pbj = models.User.objects.get(pk=4)

first()	#拿queryset第一个元素
	user_obj=models.User.objects.filter(pk=1).first()
    models.User.objects.all().first()

last()	#拿queryset最后一个元素
	models.User.objects.all().last()

values() #可以指定获取的数据字段  返回结果:列表套字典的形式 select name,age from ...
	models.User.objects.values('name')
    
values_list() #返回结果:列表套元组的形式
	 res=models.User.objects.values_list('name','age')
    
distinct()  #去重一定要是一摸一样的数据才能去重,如果带有主键那么肯定不一样
	res=models.User.objects.values('name','age').distinct()
    
order_by() #排序
	res=models.User.objects.order_by('age')  #默认升序
    res=models.User.objects.order_by('-age') #降序
    
reverse() #反转  反转的前提是已经排过序了 order_by().reverse()
	res=models.User.objects.order_by('age').reverse()
    
count() #统计当前数据的个数
	res=models.User.objects.count()
    
exclude() #排除...
	res=models.User.objects.exclude(name='zhao')

exists()  #判断是否存在,返回布尔值
	res=models.User.objects.exists()
    res=models.User.objects.filter(pk=2).exists()
	

代码:

values()
    # res = models.User.objects.values('name')
    # print(res)
    # values_list()
    
values_list()
    # res = models.User.objects.values_list('name', 'age')
    # print(res.query)  # 查看内部封装的sql语句
    """
    上述查看sql语句的方式,只能用于queryset对象
    只有queryset对象才能够.query查看内部的sql语句
    """
    
order_by()
    # res=models.User.objects.order_by('age')
    # print(res)
    
reverse()    
    res=models.User.objects.order_by('age').reverse()
    print(res)
    
count()    
    res=models.User.objects.count()
    print(res)
    
exclude    
    # models.User.objects.exclude(name='zhao')
    # res = models.User.objects.all().first()
    # print(res)

3、Django终端打印SQL语句

#方式一,只能用于queryset对象
    res = models.User.objects.values_list('name', 'age')
    print(res.query)  # 查看内部封装的sql语句
    """
    上述查看sql语句的方式,只能用于queryset对象
    只有queryset对象才能够.query查看内部的sql语句
    """
    
#方式二,所有的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',
        },
    }
}

image-20221026180126520

4、神奇的双下划线

"""神奇的双下划线"""
#年龄大于18
res=models.User.objects.filter(age__gt=18)

#年龄小于18
res=models.User.objects.filter(age__lt=18)

#年龄小于等于18
res=models.User.objects.filter(age__lte=18)

#年龄大于等于18
res=models.User.objects.filter(age__gte=18)

# 年龄在18 获取 32获取40
res=models.User.objects.filter(age__in=[18,32,40])

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

# 查询名利里含有o的数据 模糊查询   区分大小写
res = models.User.objects.filter(name__contains='o')
print(res)

# 忽略大小写
res=models.User.objects.filter(name__icontains='p') #'ignore'

# 以什么开头
res = models.User.objects.filter(name__startswith='j')

# 以什么结尾
res = models.User.objects.filter(name__endswith='j')


res=models.User.objects.filter(register_time__month='1')
res=models.User.objects.filter(register_time__day='12')
res=models.User.objects.filter(register_time__week_day='1')
res=models.User.objects.filter(register_time__year='2000')

5、多表操作

5. 1、表准备

from django.db import models

# Create your models here.

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
    # 一对多
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)

    # 多对多
    author = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()  # 本质还是varchar(254)


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    # 一对一
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    phone = models.BigIntegerField()  # 电话号码用BigIntegerField()或者用CharFiled()
    addr = models.CharField(max_length=32)

    
    
"""切记执行表的迁移命令"""    

5. 2、一对多外键增删改

 #tests.py
    # 一对多的外键增删改查
    # 增
    # 1直接写实际字段 id
    # models.Book.objects.create(title='西游记', price=81.8, publish_id=1)
    # models.Book.objects.create(title='聊斋', price=61.8, publish_id=2)
    # models.Book.objects.create(title='老子', price=66.8, publish_id=1)
    
    # 2虚拟字段  对象
    # publish_obj = models.Publish.objects.filter(pk=2).first()
    # models.Book.objects.create(title='红楼梦',price=80,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)

5. 3、多对多外键增删改

add  给第三张关系表添加数据,括号内既可以传数字,也可以传对象,并且都支持多个

remove  括号内既可以传数字,也可以传对象,并且都支持多个

set	  括号内必须传一个可迭代对象,括号内既可以传数字,也可以传对象,并且都支持多个,   先删除后新增

clear 清空对应的关系数据 括号内不加任何参数   
#tests.py

    """多对多 增删改查  就是在操作第三张表"""

    # 如何给数据添加作者
    # book_obj = models.Book.objects.filter(pk=2).first()
    # print(book_obj.author) #就类似于你已经找到了第三张关系表了
    # book_obj.author.add(1)  # 书籍id为2的书籍绑定一个主键为1的作者
    # book_obj.author.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.author.add(author_obj)
    # book_obj.author.add(author_obj1,author_obj2)
    """
    add 给第三张关系表添加数据,括号内既可以传数字,也可以传对象,并且都支持多个
    """

    # 删
    book_obj = models.Book.objects.filter(pk=2).first()

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

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

    # 修改
    # book_obj.author.set([1, 3])  # 括号内必须给一个可迭代对象
    
    # author_obj=models.Author.objects.filter(pk=2).first()
    # author_obj1=models.Author.objects.filter(pk=3).first()
    # book_obj.author.set([author_obj,author_obj1])
    """
    set 括号内必须传一个可迭代对象,括号内既可以传数字,也可以传对象,并且都支持多个,
        先删除后新增
    """

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

5. 4、正反向概念

外键字段在我手上,那么我查你就是正向
外键字段不在我手上,我查你就是反向

book>>>外键字段在书那(正向)>>>publish
pupblish >>>外键字段在书那(反向)>>>book


"""
正向查询按字段
反向查询按表名小写
"""

5. 5、多表查询

1. 子查询(基于对象的跨表查询)
   # 基于对象的跨表查询
    # 1.查询书籍主键为1的出版社
    # book_obj = models.Book.objects.filter(pk=1).first()
    # # 书查出版社》》正向
    # res = book_obj.publish
    # print(res.name)
    # print(res.addr)

    # 2.查询书籍主键为1的作者
    # book_obj = models.Book.objects.filter(pk=1).first()
    # 书查作者 正向
    # res=book_obj.author   #app01.Author.None
    # res=book_obj.author.all()#<QuerySet [<Author: Author object (1)>, <Author: Author object (3)>]>
    # print(res)

    # 3查询作者zhao的电话号码
    # author_obj = models.Author.objects.filter(name='zhao').first()
    # res = author_obj.author_detail
    # print(res.phone)
    # print(res.addr)


    """
    在书写orm语句的时候,跟写sql 语句是一样的
    不要企图一次性写完,如果比较复杂,就写一点看一点
    
    正向什么时候需要加.all()????
        当结果可能有多个,就需要加.all()
        如果是一个则直接拿到数据对象
     """
   
    # 4查询出版社是东方出版社出版的书
    # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    # 出版社查书 反向
    # res = publish_obj.book_set.all()
    # print(res)

    # 5.查询作者是zhao写过的书
    # author_obj = models.Author.objects.filter(name='zhao').first()
    # 作者查书,,反向
    # ress = author_obj.book_set.all()
    # print(ress)

    # 6手机号是110的作者姓名
    # author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    # res=author_detail_obj.author
    # print(res.name)

    
 """
    基于对象
        反向查询的时候    
            当你的查询结果可以有多个的时候,就必须加_set.all()
            当结果只有一个的时候,就不许需要加_set.all()
        
 """
2.联表查询(基于双下划线的跨表查询)
  """基于双下划线的跨表查询"""
    
    # 1.查询zhao的手机号
    # res = models.Author.objects.filter(name='zhao').values('author_detail__phone')
    # print(res)
    """反向"""
    # res = models.AuthorDetail.objects.filter(author__name='zhao')  # 拿作者姓名是zhao的作者详情
    # res = models.AuthorDetail.objects.filter(author__name='zhao').values('phone','author__name')  # 拿作者姓名是zhao的作者详情
    # print(res)

    # 2查询书籍主键为1的出版社名称和书的名称
    # res = models.Book.objects.filter(pk=1).values('title', 'publish__name')
    # print(res)
    """反向"""
    # res=models.Publish.objects.filter(book__pk=1).values('name','book__title')
    # print(res)

    # 3查询书籍主键为1的作者姓名
    # res=models.Book.objects.filter(pk=1).values('author__name')
    # print(res)
    """反向"""
    # res=models.Author.objects.filter(book__pk=1).values('name')
    # print(res)

    # 4查询书籍主键是1的作者的手机号
    # book author authordetail
    # res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
    # print(res)  
    """反向"""
   #res1=models.Author.objects.filter(book__id=1).values('author_detail__phone')
    # print(res1)

6、聚合查询

"""聚合查询   aggregate"""

"""聚合函数通常都是配合分组一起使用的,也可以单独使用,需要借助于aggregate"""

from django.db.models import Max, Min, Sum, Count, Avg

# 只要是跟数据库相关的模块,
	#基本上都在,django.db.models里面,
    #如果没有那么就应该在django.db里面
    
# 统计书的平均价格
# res=models.Book.objects.aggregate(Avg('price'))
# print(res)
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'))
print(res)

7、分组查询

"""分组查询 annotate"""
"""
    MySQL分组之后默认只能获取到分组的依据,组内其他字段都无法直接获取了, 
        严格模式
            ONLY_FULL_GROUP_BY
    
    
"""
# 1统计每一本书的作者个数
from django.db.models import Max, Min, Sum, Count, Avg
# res = models.Book.objects.annotate()  # models后面点什么,就是按什么分组
# res = models.Book.objects.annotate(author_number=Count('author__id')).values('title','author_number')
# res1 = models.Book.objects.annotate(author_number=Count('author')).values('title','author_number')
"""
author_number是自己定义的字段,用来存储统计出来的每本书对应的作者个数
"""
# print(res1)
# 2统计每个出版社卖的最便宜的书的价格
# res = models.Publish.objects.annotate(bargain=Min('book__price')).values('name','bargain')
# print(res)

# 3统计不止一个作者的图书
# 1.先按照图书分组,求每一本书对应的作者个数
# 2过滤出不止一个作者的图书
# res = models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('title', 'author_num')

"""只要你的orm 语句得出的结果还是一个queryset对象那么就可以无限制的点queryset方法"""
# print(res)

# 4查询每个作者出版的书的总价格
# res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
# print(res)

"""
如果向按照指定的字段分组
    models.Book.objects.values('price').annotate()
"""

"""
如果分组查询报错,
    需要修改数据严格模式
"""

8、F与Q查询

8. 1、F查询

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

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

    # 3将所有数的名称后面加上爆款两个字
    """
    在操作字符类型的数据的时候,F不能够直接做到字符串的拼接
    """
    from django.db.models import Value
    from django.db.models.functions import Concat
    res = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
    print(res)

8. 2、Q查询

 """Q查询"""
    """能够改变多个查询条件之间的关系:与或非"""
    
    
    from django.db.models import Q
    # 1查询卖出数大于200或者价格小于50的书籍
    # res = models.Book.objects.filter(Q(maichu__gt=200),Q(price__lt=50))
    # print(res)
    """Q包裹,逗号分割还是and关系,"""
    # res = models.Book.objects.filter(Q(maichu__gt=200)|Q(price__lt=50)) #| or关系
    """竖杠就是or关系"""
    # res = models.Book.objects.filter(~Q(maichu__gt=200) | Q(price__lt=50))  # ~ not关系
    """~就是not关系"""
    # print(res)

    
    """Q高阶用法  能够将查询条件变成字符串的形式"""
    q = Q()
    q.connector='or'
    q.children.append(('maichu__gt', 200))
    q.children.append(('price__lt', 50))
    res = models.Book.objects.filter(q)  # filter内支持直接放q对象,默认还是and关系
    print(res)

9、Django中开启事务

"""
MySQL中:
    事务
        ACID
            A:原子性	不可分割的最小单位
            C:一致性	跟原子性是相辅相成的
            I:隔离性	事务之间互相不干扰
            D:持久性	事务一旦确认永久生效

        事务的回滚
            roollback
        事务的提交
            commit

"""

#django中如何开启事务
    from django.db import transaction
    try:
        with transaction.atomic():
            # sql语句
            # with代码块内书写的所有orm操作都是属于同一个事务
            pass
    except Exception as e:
        print(e)

10、orm中常用字段及参数

AutoField	
	主键字段 primary_key=True
CharField	
	max_length字符长度  verbose字符的注释  对应到数据库是varchar

IntegerField  	    int
BigIntegerField  	bigint

DecimalField  
	max_digits=8, decimal_places=2
    
EmailField  varchar(254)

DateField         date
DateTimeField	  datetime
	auto_now :每次修改数据都会自动更新当前时间
    auto_now_add:只在创建数据的时候记录时间,后续不会修改

    
BooleanField  布尔类型
	该字段传布尔值(False/True),数据库里存0/1
    
TextField   文本类型
	该字段可以存大段内容(文章、博客....),没有字数限制
FileField
    	upload_to='/data'  给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中/data/a.txt


"""其他字段参考博客"""
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
        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)

11、数据库查询优化

only与defer  正好相反
select_related与prefetch_related  跟跨表操作有关
"""

orm语句的特点:惰性查询,如果仅仅只是书写了orm语句,在后面没有用到该语句所查询出来的参数,那么orm 会自动识别,直接不执行
"""

only

    res=models.Book.objects.only('title')
    # res=models.Book.objects.all()
    # print(res)
    for i in res:
        # print(i.title) #点击only括号内的字段,不会走数据库
        print(i.price) #点击only括号外的字段,会重新走数据库查询而all不需要

image-20221029164727597


image-20221029164841977

defer

#tests.py

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

image-20221029164310790


image-20221029164520153


    res = models.Book.objects.select_related('publish')  # INNER JOIN
    """
    select_related内部直接将book表与publish表连接起来,然后一次性将大表里面的所有数据全部封装给查询出来的对象
    这个时候对象无论是点击book表的数据还是publish数据都无需再走数据查询了
    
    
    select_related括号内只能放外键字段  
        只能放一对一 ,一对多,
        不能放多对多
    
    """
    # print(res)
    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)

posted @ 2022-12-12 15:52  ExpiredSaury  阅读(19)  评论(0编辑  收藏  举报