Django模型层

Posted on 2022-11-27 21:37  呱呱呱呱叽里呱啦  阅读(14)  评论(0编辑  收藏  举报

Django模型层

测试脚本

# 如果只想测试django中某一个py文件,则不用书写全部前后端交互的代码,而是直接写一个脚本即可
# 测试环境准备,在test.py中:
import os

if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django模型层.settings') #修改项目名
    import django
    django.setup()
    # 此处写测试代码,注意缩进

单表操作

from app01 import models
    # 增
    # models.User.objects.create(name='jason',age=19,register_time=timezone.now()) #返回被创建的对象
    # user_obj = models.User(name='egon',age=20,register_time='2020-11-11')
    # user_obj.save()

    # 删
    # res = models.User.objects.filter(pk=2).delete() #批量删除
    # del_obj = models.User.objects.filter(pk=3).first()
    # del_obj.delete() #单独删除

    # 改
    # models.User.objects.filter(pk=4).update(name='egon') #批量改

    # 查
    '''
    1.all() 查询所有
    2.filter() 带有过滤条件查询
    3.get() 查询数据对象,条件有误直接报错
    4.first() 拿QuerySet第一个元素
    5.last() 拿QuerySet第一个元素
    6.values() 

    res = models.User.objects.values('name','age)
    返回仅带有指定字段的QuerySet(列表套字典)

    7.values_list()

    res = models.User.objects.values_list('name','age)
    返回仅带有指定字段的QuerySet(列表套元组)
    print(res.query) QuerySet对象可以使用.query查看内部sql语句

    8.distinct()
    去重一定要是一模一样的数据,所以要排除主键

    9.order_by()
    res = models.User.objects.order_by('age')
    默认升序
    res = models.User.objects.order_by('-age') 降序

    10.reverse()
    先排序,后翻转

    11.count()
    res = models.User.objects.count()
    12.exclude()
    res = models.User.objects.exclude(name='jason)
    排除符合参数条件的记录
    13.exists()
    res = models.User.objects.filter(name='jason).exists()
    '''

查看内部sql语句的方式

# 一、QuerySet对象可以使用.query查看内部sql语句
res = models.User.objects.values_list('name','age)
print(res.query)
# 二、配置settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

双下划线查询

res = models.User.objects.filter(age__gt=20) # 年龄大于20
res = models.User.objects.filter(age__lt=20) # 年龄小于20
res = models.User.objects.filter(age__gte=20) # 年龄大于等于20
res = models.User.objects.filter(age__in=[10,20,30]) # 年龄等于10或者20或者30
res = models.User.objects.filter(age__range=[18,40]) # 年龄大于等于18小于等于40
res = models.User.objects.filter(name__contains='n') # name字段包含'n'的,默认区分大小写
# 忽略大小写:
res = models.User.objects.filter(name__icontains='n')
# ignore
res = models.User.objects.filter(name__startwith='n') # name字段以'n'开头的
res = models.User.objects.filter(name__endwith='n')

res = models.User.objects.filter(register_time__month='12') # 注册时间为12月份的
# 好多方法自己点出来看啊

外键增删改

# 一对多外键增删改
# 增
models.Book.objects.create(name='三国',price=123.23,publish_id=1)

pub_obj = models.Publish.objects.filter(pk=3).first()
models.Book.objects.create(name='红楼', price=123.00, publish=pub_obj)

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

# 改
models.Book.objects.filter(pk=5).update(publish_id=4)
models.Book.objects.filter(pk=5).update(publish=pub_obj)


# 多对多外键增删改,即对中间表的操作
# 增

book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(1,6,7)

book_obj = models.Book.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=6).first()
book_obj.authors.add(author_obj1,author_obj2)

# 删
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.remove(7) # 同样可以传对象,支持多个

# 改

book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.set([1,8])# 参数需要是可迭代对象,参数内同样可以传对象,支持多个,set不会改变与参数重复的原纪录,会删去不在参数中的纪录,不回收主键值,另新建只在参数中有的纪录

# 清空,即在中间表中清空某本书籍与作者的绑定关系
book_obj = models.Book.objects.filter(pk=5).first()
book_obj.authors.clear() # 无需传参

正反向

# 正向 由外键所在表向外键关联表查询即为正向
# 反向 由外键关联表向外键所在表查询即为反向

多表查询

# 基于对象的跨表查询,即子查询,正向查询用字段,反向查询用小写表名

models.Book.objects.filter(pk=1).first().publish.name # 查图书表主键为一的书的出版社

models.Book.objects.filter(pk=1).first().authors.all() # 查符合条件的书籍的所有作者

pub_obj = models.Publish.objects.filter(name='东方出版社').first()
pub_obj.book_set.all() # 查询东方出版社出版的书籍

# 反向查询时,当查询结果有可能有多个时,需要_set.all(),一对一结果不需要

# 基于双下划线的跨表查询,即连表查询
models.Author.objects.filter(name='吴承恩').values(‘name','author_info__phone') #查询吴承恩手机号,返回QuerySet

# 可以将返回的QuerySet当做mysql中的结果表,返回的一个不可分对象当做一条记录。
# filter()中的字段名作为关键字不需用'',而values中的字段名需要''                                   

# publish是虚拟字段,publish_id是django自动添加后缀在mysql生成的字段,.publish返回的是此外键关联的publish对象,而.values('publish_id')返回的是该条记录此字段的值

聚合查询

from django.db.models import Max,Min,Sum,Count,Avg # 一般情况下与分组查询一起使用
#单独使用聚合查询 aggregate

分组查询

# annotate
models.Book.objects.annotate(a_num=Count('authors')).values('name','a_num') #查询每本书的作者数量
print(models.Book.objects.aggregate(Avg('price'))
print(models.Book.objects.annotate(a_num=Count('authors')).values('name','a_num'))
     print(models.Publish.objects.annotate(l_price=Min('book__price')).values('name','l_price'))
     print(models.Book.objects.annotate(a_num=Count('authors')).filter(a_num__gt=1).values('name','a_num'))
     print(models.Author.objects.annotate(amount=Sum('book__price')).values('name','amount'))

# 按照字段分组查询
models.Author.objects.values('price').annotate()

F与Q查询

# F('') 直接获取表中某字段的数据返回到代码位置
# 
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(name=Concat(F('name'),Value('文学大师推荐')))
from django.db.models import Q
   print(models.Book.objects.filter(Q(sold__gt=2000)|Q(price__lt=100)).values('name','sold','price')) # or=|,and=,,not=~

q = Q()
q.connector = 'or' # 设置条件连接关系
q.children.append(('sold__gt',2000)) # 设置比较条件,注意传入的是一个元组
q.children.append(('price__lt',100))
print(models.Book.objects.filter(q).values('name','sold','price'))
# 即SELECT `app01_book`.`name`, `app01_book`.`sold`, `app01_book`.`price` FROM `app01_book` WHERE (`app01_book`.`sold` > 2000 or `app01_book`.`price` < 100) LIMIT 21; args=(2000, Decimal('100'))

开启事务

# 事务的ACID:原子性,一致性,隔离性,持久性
# 事务的回滚rollback、确认commit
from django.db import transaction
with transaction.atomic():
    # 内部所有ORM操作同属于一个事务 

ORM常用字段及参数

AutoField #主键字段 primary_key=True
CharField #即varchar verbose字段注释,max_lenth长度
IntegerField #即int
BigIntegerField #即BigInt
DecimalField #max_digits,decimal_places
EmailField #varchar(254)
DateField #即Date auto_now/auto_now_add
DateTimeField #即DateTime
BooleanField #即布尔值,传False/True,存0/1
TextField #没有字数限制
FileField #upload_to '' 
# 自定义字段
# 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 f'char({self.max_length})'


#null:即是否可以为空
#unique:是否唯一
#db_index:为字段建立索引
#default:设置字段默认值
#外键字段
#to_field:默认关联另一张表的主键字段
#on_delete:2.x一对多一对一需要手动设定级联

数据库查询优化

# ORM惰性查询,如果查询结果没有被使用,则不执行查询语句
# only/defer
models.Book.objects.only('name') # 返回包含name字段信息的对象组成的QuerySet,但仍可以.其他字段,只不过会重新查询数据库
models.Book.objects.defer('name') # 返回不包含name字段信息的对象组成的QuerySet,但仍可以.name字段,只不过会重新查询数据库

#only与defer一种是返回只包含参数字段对象组成的QuerySet,一种是返回不包含参数字段对象组成的QuerySet,如果要使用.字段访问信息,则当返回的QuerySet中没有此字段信息时需要重新查询数据库


# select_related(连表查询)/prefetch_ralated(子查询)

res = models.Book.objects.all()
for i in res:
    print(i.publish.name) # 每循环一次都查询一次数据库

res = models.Book.objects.select_related('publish') # 只在这里连表查询一次数据库、参数只能是一对多一对一外键字段,只支持正向查询
for i in res:
    print(i.publish.name) # 这里使用上面的连表查询结果,不用每次都查询数据库

res = models.Book.objects.prefetch_related('publish')
for i in res:
    print(i.publish.name)

# 要结合具体需求,选择查询方法,select_related返回的对象结果可能过大,而prefetch_related查询的次数可能更多。

图书管理系统

# redirect参数可以直接传URL别名,但使用带正则的别名必须使用reverse()
# 见项目代码

choices参数

# 如果某个字段的值是可以列举完全的,那么通常会采用choices参数
gender_choices = (
    (1,'男'),
    (2,'女'),
    (3,'其他')
)
gender = models.IntegerField(choices=gender_choices) # 该gender字段仍存数字,但是如果数字值在gender_choices内,那么可以轻松获取数字对应的真正内容,字段类型由元组内第一个参数类型决定

# gender字段可以存储不在gender_choices内的值,范围参考数据类型

# 获取对应信息 get_字段名_display
user_obj = models.User.objects.filter(pk=1).first()
user_obj.get_gender_display()
# 如果无对应值,则返回原值

MTV与MVC模型

# MTV,即models、templates、views
# MVC,即models、views、controller

多对多三种创建方式

class BooK
class Author

# 全自动:利用ORM自动创建中间表,可以使用ORM封装好的操作,但无法使用ORM拓展
authors = models.ManyToManyField(to='Author')

# 半自动:可以使用ORM正反向查询,但无法使用add、set、remove、clear四个方法,为了拓展性,一般半自动
class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
class Book
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_field=('book','author')) # 元组顺序由中间表查询本表、被连接表的键决定,可以判断为先自后它



# 全手动:手动创建表,无法使用ORM封装好的操作,容易拓展
class Book_Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')