第六篇:Django的模型层

早过忘川 提交于 2020-01-09 22:36:56

ORM查询

1、只要是queryset对象就可以无限制调用Queryset对象的方法:

res = models.User.objects.filter().filter().update()....

2、只要是queryset对象就可以通过下面的语句获得sql语句:

print(res.query)

3、如果不是queryset对象,也可以获得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',
        },
    }
}

单独测试Django部分功能

当测试Django的时候,我们往往只需要测试部分功能,启动整个Django显得太慢,太大,因此我们可以书写测试脚本:

步骤如下:

1、在任意目录下创建一个py文件进行书写,或者直接在app下的tests.py文件中书写以下代码:

# 以下代码可以去manage.py文件中复制
import os
 
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")
    import django
    django.setup()

2、示范,例如我想对模板层进行测试,代码如下:

import os
 
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")
    import django
    django.setup()
    # 导入models
    from app01 import models
    # 调用models相关功能
    models.Movie.objects.all()

单表查询

在对models进行测试的时候,我们需要配置数据库的相关参数(settings.py)以及在__init__.py中导入pymysql,在models.py文件中创建我们的模型表,步骤如下:

1、在models.py中创建模型表

class Movie(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # auto_now_add=True 当对数据进行创建的时候会自动添加时间
    publish_time = models.DateField(auto_now_add=True)

2、执行迁移数据库命令

python manage.py makemigrations
 
python manage.py migrate

基础的16条

  • create() 返回值是当前创建的数据对象本身
  • all() queryset对象
  • filter() queryset对象
  • update() 更新数据
  • delete() 删除数据
  • first() 取第一子元素
  • last() 取最后一个子元素
  • get() 数据对象,不推荐使用,条件不存在直接报错
  • values() queryset对象 看似:列表套字典
  • values_list() queryset对象 看似:列表套元组
  • order_by() 排序
  • count() 计数
  • exclude() 排除某个字段在外
  • exists() 判断结果是否存在数据,返回的是bool值
  • reverse() 反转
  • distinct() 去重,必须是完全重复的数据
from django.test import TestCase
 
# Create your tests here.
 
import os
 
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mystile.settings")
    import django
    django.setup()
    from test_func import models
    from datetime import date
    ctime = date.today()
    # 还可以直接传入日期时间对象
    # 1、create() 创建数据,得到的返回值就是创建数据的对象本身,可以直接使用res
    res = models.Movie.objects.create(title='山楂树之恋', price=999, publish_time=ctime)
 
    # 2、all()  查询表中的所数据,返回的是queryset对象, 类似列表套对象
    res = models.Movie.objects.all()
    print(res)
 
    # 3、filter() 按照指定条件去查询,返回的是queryset对象,类似列表套对象
    res = models.Movie.objects.filter(id=1)
    print(res)
 
    # 只要是queryset对象,就可以通过 点query 的方式查看sql语句
    res = models.Movie.objects.filter(title='山楂树之恋')
    print(res.query)
 
    # 4、get()  返回的是对象本身,不推荐使用,因为当查询条件不存在的时候会报错
    res = models.Movie.objects.get(pk=1)
    print(res)
 
    # 5、values() 获取指定字段对应的数据,列表套字典     QuerySet对象
    res = models.Movie.objects.values('title', 'price', 'publish_time')
    print(res)
 
    # 6、values_list() 获取指定字段对应的数据,元组套字典   QuerySet对象
    res = models.Movie.objects.values_list('title', 'price')
    print(res)
 
    # 7、first()  获取第一个元素对象,返回的是数据对象
    res = models.Movie.objects.filter().first()
    print(res)
 
    # 8、last()  获取最后一个元素对象,返回的是数据对象
    res = models.Movie.objects.filter().last()
    print(res.title)
 
    # 9、update()  更新数据    返回值是受影响的行数
    res = models.Movie.objects.filter(pk=2).update(title='大鱼海棠')
    print(res)
 
    # 10、delete()  删除数据     返回值(1, {'app01.Movie': 1})  受影响的表及行数
    res = models.Movie.objects.filter(pk=2).delete()
    print(res)
 
    # 11、count()  统计数据条数
    res = models.Movie.objects.count()
    print(res)
    res = models.Movie.objects.filter(pk=2).count()
    print(res)
 
    # 12、order_by()  按照指定字段排序
    res = models.Movie.objects.order_by('price')  # 默认升序
    res1 = models.Movie.objects.order_by('-price')  # 降序
    print(res)
    print(res1)
 
    # 13、exclude()  排除什么什么之外的数据
    res = models.Movie.objects.exclude(pk=1).first()
    print(res.title)
 
    # 14、exists()  判断查询是够有数据,返回bool值
    res = models.Movie.objects.filter(pk=10000).exists()
    print(res)
 
    # 15、reverse()  反转
    res = models.Movie.objects.order_by('price').reverse()
    print(res)
 
    # 16、distinct()  去重:去重的前提,必须是由完全一样的数据才能去重
    # 因此,这里要注意将django自动创建的主键排除在外
    res = models.Movie.objects.values('title', 'price').distinct()
    print(res)

双下划线查询

  • __gt 大于
  • __lt 小于
  • __gte 大于等于
  • __lte 小于等于
  • __in 或
  • __range 某个范围内,顾头也顾尾

模糊查询:

  • __ contains 区分大小写
  • __icontains 忽略大小写

年月日:

  • create_time__year
  • create_time__month

双下划线查询,一般用于括号内,对数据进一步进行限制,比如求商品的价格大于200的...

    # 双下划线查询
    # 1、查询价格大于200的电影
    res = models.Movie.objects.filter(price__gt=200)
    print(res)
 
    # 2、查询价格小于500的电影
    res = models.Movie.objects.filter(price__lt=500)
    print(res)
 
    # 3、查询价格大于等于200的电影
    res = models.Movie.objects.filter(price__gte=200)
    print(res)
 
    # 4、查询价格小于等于400的电影
    res = models.Movie.objects.filter(price__lte=500)
    print(res)
 
    # 5、查询价格在200到500之间的电影, 包括200到500
    res = models.Movie.objects.filter(price__in=(200, 500))
 
    # 6、查询价格是100,或66,或521
    res = models.Movie.objects.filter(price__in=[100, 66, 521])
 
    # 7、查询电影名字中包含字符“山楂树”的电影
    '''
    模糊查询:
        关键字:like
        关键符:%和-
    '''
    res = models.Movie.objects.filter(title__contains='山楂树')  # 区分大小写
    res = models.Movie.objects.filter(title__icontains='山楂树')  # 多加i可以忽略大小写
    print(res)
 
    # 8、查询2020年出版的电影
    res = models.Movie.objects.filter(publish_time__year=2020)
    print(res)
 
    # 9、查询是1月份出版的电影
    res = models.Movie.objects.filter(publish_time__month=1)
    print(res)

多表查询

图书管理系统表创建

针对多表查询,我们需要添加更多的表,步骤如下:

1、models.py中创建模型表

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # 该字段新增数据自动添加 无需考虑
    publish_time = models.DateField(auto_now_add=True)
 
    # 出版社   一对多(一个出版社可以出版多个书,一本书不可以被多个出版社出版)   外键字段建在多的一方
    publish = models.ForeignKey(to='Publish')
 
    # 作者 多对多关系
    # 一本书可以有多个作者,一个作者也可以写多本书
    authors = models.ManyToManyField(to='Author')
 
 
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=255)
 
 
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
 
    # 作者详情  一对一关系
    author_detail = models.OneToOneField(to='AuthorDetail')
 
 
class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=255)

2、执行数据库迁移命令

python manage.py makemigrations
 
python manage.py migrate

外键字段的增删改查

一对多

  • publish 传数据对象
  • publish_id 传数据主键值
# 1、增 直接写实际的表字段 publish_id
    models.Book.objects.create(title='三国演义', price=123.23, publish_id=2)
    # 2、增 先查出来要绑定的出版社对象
    publish_obj = models.Publish.objects.get(pk=1)
    # 将查出来的对象赋值给publish
    models.Book.objects.create(title='水浒传', price=66.6, publish=publish_obj)
 
    # 查
    # 在下面的多表查询中
 
    # 改
    # 第一种方法
    models.Book.objects.filter(pk=2).update(publish_id=3)
    # 第二种方法,依旧是拿对象赋值
    publish_obj = models.Publish.objects.get(pk=3)
    models.Book.objects.filter(pk=2).update(publish=publish_obj)
 
    # 删
    '''
    外键字段在1.X版本中默认就是级联更新级联删除的
    2.X版本中,则需要你自己手动指定
        百度一大堆
    当没有级联删除和级联更新的情况下,只有删除关联表中的数据,才能删除和修改被关联表中的数据
    当有级联删除和级联更新的情况下,修改和删除被关联表中的数据, 关联表中的数据也会跟着被删除和修改
    '''

多对多

add()

add 专门给多对多种的第三张虚拟表添加数据,括号内可以传数字,也可以穿对象,并且都支持传多个。

  # 多对多
    # 1、给书籍绑定作者关系  add
    book_obj = models.Book.objects.filter(pk=2).first()
    # 书籍和作者的关系是由第三张表决定,那就意味着需要操作第三张表
    print(book_obj.authors)  # 书籍对象.虚拟字段authors就类似已经跨到了书籍和作者的第三张表
    book_obj.authors.add(1)  # 给书籍绑定一个主键为1的作者
    book_obj.authors.add(2, 3)
 
    # 第二种方法,直接添加对象
    book_obj = models.Book.objects.filter(pk=3).first()
    author_obj = models.Author.objects.get(pk=1)
    author_obj1 = models.Author.objects.get(pk=3)
    book_obj.authors.add(author_obj)
    book_obj.authors.add(author_obj, author_obj1)

remove()

专门给第三张关系表移除数据,括号内既可以传数字也可以传对象,

并且都支持传多个。

    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.remove(1)
    book_obj.authors.remove(2, 3)
 
    author_obj = models.Author.objects.get(pk=1)
    author_obj1 = models.Author.objects.get(pk=3)
    book_obj.authors.remove(author_obj)
    book_obj.authors.remove(author_obj, author_obj1)

set()

修改书籍与作者的关系

括号内支持传数字和对象,但是需要是可迭代对象

    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.set((3,))
    book_obj.authors.set((1, 2))
    authors_obj = models.Author.objects.get(pk=1)
    authors_obj1 = models.Author.objects.get(pk=2)
    book_obj.authors.set((authors_obj,))
    book_obj.authors.set([authors_obj, authors_obj1])

clear()

清空关系

不需要任何参数

    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.clear()  # 去第三张表中清空书籍为1的所有书籍

跨表查询

跨表查询的方式

1、子查询

将一张表的查询结果当做另一张表的查询条件

2、链表查询

  • inner join
  • left join
  • right join
  • union

建议:在写sql语句或者orm语句的时候不要想着一次性将语句写完,一定要写一点查一点,再写一点

正反向的概念

正向:

跨表查询的时候 外键字段是否在当前数据对象中 如果在查询另外一张关系表 叫正向

反向:

如果不在叫反向

口诀:

正向查询按外键字段

反向查询按表名小写

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

正向查询的时候,当外键字段对应的数据可以有多个的时候需要加.all()

否则点外键字典即可获取到对应的数据对象

    # 1、查询书籍pk为1的出版社名称
    book_obj = models.Book.objects.filter(pk=2).first()
    print(book_obj.publish)
    print(book_obj.publish.name)
    print(book_obj.publish.addr)
 
    # 2、查询书籍pk为2的所有作者的姓名
    book_obj = models.Book.objects.filter(pk=3).first()
    print(book_obj.authors)  # test_func.Author.None
    print(book_obj.authors.all())
    author_list = book_obj.authors.all()
    for author_obj in author_list:
        print(author_obj.name)
 
    # 3、查询作者pk为1的电话号码
    author_obj = models.Author.objects.filter(pk=1).first()
    print(author_obj.author_detail)
    print(author_obj.author_detail.phone)
    print(author_obj.author_detail.addr)

基于对象的反向查询 表名小写是需要加_set.all()

  • 一对多和多对多的时候需要加
  • 一对一不需要
    # 4、查询出版社名称为东方出版社出版过的书籍
    publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    print(publish_obj.book_set)  # test_func.Book.None
    print(publish_obj.book_set.all())
 
    # 5、查询作者为engon写过的书
    author_obj = models.Author.objects.filter(name='engon').first()
    print(author_obj.book_set)  # app01.Book.None
    print(author_obj.book_set.all())
 
    # 6、查询手机号为120的作者姓名
    author_detail_obj = models.AuthorDetail.objects.filter(phone=120).first()
    print(author_detail_obj.author)
    print(author_detail_obj.author.name)
    print(author_detail_obj.author.age)

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

只要表之间有关系,你就可以通过正向的外键字段或者反向的表名小写,连续跨表查询

正向查询:先获取有外键字段的对象,通过对象的values内,使用外键字段__到另外一张表中取值

反向查询:先获取另一张表的对象,在filter括号内,通过表名小写__到另外一张表中,得到另外一张表中的字段加限制条件,在.value的括号内,可以直接取本对象的value,以及通过双下划线__取到另外一张表的value。

    # 1、查询书籍pk为2的出版社名称
    # 正向
    # 写外键字段,就意味着你已经在外键字段管理的那张表中
    res = models.Book.objects.filter(pk=2).values('publish__name')
    print(res)
    # 反向
    # 拿出版过pk为1的书籍对应的出版社
    res = models.Publish.objects.filter(book__pk=2)
    res = models.Publish.objects.filter(book__pk=2).values('name')
    print(res)
 
    # 2、查询书籍pk为2的作者姓名和年龄
    # 正向
    res = models.Book.objects.filter(pk=3).values('title', 'authors__name', 'authors__age')
    print(res)
    # 反向
    res = models.Author.objects.filter(book__pk=2)  # 拿出出版过书籍pk为1的作者
    res = models.Author.objects.filter(book__pk=2).values('name', 'age', 'book__title')
    print(res)
 
    # 3、查询作者是engon的年龄和手机号
    # 正向
    res = models.Author.objects.filter(name='engon').values('age', 'author_detail__phone')
    # 反向
    res = models.AuthorDetail.objects.filter(author__name='engon').values('author__age', 'phone')
    print(res)
 
    # 4、查询书籍pk为的2的作者的手机
    # 正向
    res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
    print(res)
    # 反向
    res = models.AuthorDetail.objects.filter(author__book__pk=2).values('phone')
    print(res)

聚合查询

关键字:

aggregate

模块的导入:

from django.db.models import Max, Min, Avg, Count, Sum
  • Max 查询最大值
  • Min 查询最小值
  • Avg 求平均值
  • Count 统计
  • Sum 求和

范例:

     from django.db.models import Max, Min, Avg, Count, Sum
    # 查询所有书的平均价格
    res = models.Book.objects.aggregate(avg_num=Avg('price'))
    print(res)
    # 查询价格最贵的书籍
    res = models.Book.objects.aggregate(max_price=Max('price'))
    print(res)
    # 全部使用一遍
    res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'), Count('pk'), Sum('price'))
    print(res)

分组查询

关键字:

annotate

模块导入:

from django.db.models import Max,Min,Sum,Count,Avg
    # 1、统计每一本书的作者个数
    res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
    print(res)
 
    # 2、统计出每一个出版社卖的最便宜的书的价格
    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'book__title', 'book__price')
    print(res)
 
    # 3、统计不止一个作者的书
    res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title')
    print(res)
 
    # 4、查询各个作者出的书籍的总价格
    res = models.Author.objects.annotate(author_num=Sum('book__price')).values('name', 'author_num')
    print(res)
 
    # 5、如何按照表中的某一指定字段分组
    res = models.Book.objects.values('price').annotate()  # 以价格字段进行分组
    print(res)

F与Q查询

需要导入模块

from django.db.models import F, Q

F

可以获取到表中的某个字段对应的值

1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(kucun__gt=F('maichu'))
print(res)
 
2.将所有书的价格提高100
res = models.Book.objects.update(price=F('price') + 100)

Q

能够改变查询的条件:

  • and
  • or
  • not
Q能够改变查询的条件关系  and or not
1.查询书的名字是python入门或者价格是1000的书籍
res = models.Book.objects.filter(title='python入门',price=1000)  # and关系
res = models.Book.objects.filter(Q(title='python入门'),Q(price=1000))  # 逗号是and关系
res = models.Book.objects.filter(Q(title='python入门')|Q(price=1000))  # |是or关系
res = models.Book.objects.filter(~Q(title='python入门')|Q(price=1000))  # ~是not关系

Q的高阶用法

Q的高阶用法,可以以用户输入的字符串变成可以查询的限制条件,因此多用于搜索框功能。

# res = models.Book.objects.filter('title'='python入门')
 
q = Q()
q.connector = 'or'  # q对象默认也是and关系  可以通过connector改变or
q.children.append(('title','python入门'))
q.children.append(('price',1000))
 
res = models.Book.objects.filter(q)
print(res)
 

models中常用字段

AutoField(primary_key=True)  主键字段
CharField(max_length=32)     varchar(32)
IntegerField()               int
BigIntergerField()           bigint
DecimalField()               decimal
EmailField()                 varchart(254)
DateField()                     date
DateTimeField()              datetime
    auto_now:每次编辑数据的时候都会自动更新该字段时间
    auto_now_add:创建数据的时候自动更新
BooleanField(Field)
    给该字段传布尔值 会对应成  数字0/1
    is_delete
    is_status
    is_vip
TextField(Field)
    - 文本类型
    存储大段文本
FileField(Field)
    - 字符串,路径保存在数据库,文件上传到指定目录,只存文件路径
    upload_to = '指定文件路径'
    给该字段传文件对象 文件会自动保存到upload_to指定的文件夹下 然后该字段存文件的路径

django默认建的字符串类型都是varchar,想要建立char字段,需要自定义char类型的字段,方法如下:

# modles.py
 
from django.db.models import Field
 
 
class RealCharField(Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length  # 拦截一个父类的方法 操作完之后 利用super调用父类的方法
        super().__init__(max_length=max_length,*args,**kwargs)
 
 
    def db_type(self, connection):
        return 'char(%s)'%self.max_length
 
 
# 使用自定义的char类型字段
class Movie(models.Model):
    textField = RealCharField(max_length=64)

字段内的关键性参数

null

null 用于表示某个字段可以为空

default

default 设置该字段的默认值

unique

如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index

如果db_index=True 则代表着为此字段设置索引。

2.x版本级联更新和级联删除

django 1.x默认就是级联更新级联删除 django2.x需要你自己手动指定

on_delete=models.CASCADE

db_contraints = True

  • 级联删除:models.CASCADE
    当关联表中的数据删除时,该外键也删除
  • 置空:models.SET_NULL
    当关联表中的数据删除时,该外键置空,当然,你的这个外键字段得允许为空,null=True
  • 设置默认值:models.SET_DEFAULT
    删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。
#级联删除情况
class UserToken(models):                             #级联删除,用户删除,它也删除
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, to_field='id')
    token = models.CharField(max_length=128, null=True)
 
 
#置空情况
class Server(models.Model):
 
    server_type_choice = (
        (1, "WEB"),
        (2, "存储"),
        (3, "缓存")
    )
 
    server_type = models.IntegerField(choices=server_type_choice)
    hostname = models.CharField(max_length=32)
    port = models.IntegerField()
    business_unit = models.ForeignKey("BusinessUnit", on_delete= models.SET_NULL, null=True)
 
 
#设置默认值
    user = models.ForeignKey("UserInfo", on_delete= models.SET_DEFAULT, default=0)
  • 两个不常用的
  • PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。
  • SET(): 自定义一个值,该值当然只能是对应的实体了

choices参数

当你数据能够被你列举完全,可以考虑使用choices参数,用数字来代替字符串存入数据库,可以节省硬盘空间。

class Userinfo(models.Model):
    username = models.CharField(max_length=32)
    gender_choices = (
        (1,'男'),
        (2,'女'),
        (3,'其他'),
    )
    gender = models.IntegerField(choices=gender_choices)
    # 该字段还是存数字 并且可以匹配关系之外的数字
    record_choices = (('checked', "已签到"),
          ('vacate', "请假"),
          ('late', "迟到"),
          ('noshow', "缺勤"),
          ('leave_early', "早退"),
          )
 
    record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
 
 
user_obj = models.Userinfo.objects.get(pk=1)
print(user_obj.username)
print(user_obj.gender)
# 针对choices参数字段 取值的时候   get_xxx_display()
print(user_obj.get_gender_display())
# 针对没有注释信息的数据  get_xxx_display()获取到的还是数字本身
user_obj = models.Userinfo.objects.get(pk=4)
print(user_obj.gender)
print(user_obj.get_gender_display())

数据库查询优化

django orm的所有查询都是惰性查询,也就意味着,不使用查询出来的结果,sql语句并不会执行,例如:

res = models.Book.objects.all()

当我不引用和使用res的时候,这条语句是不会执行。

only与defer

only的作用:

括号内传字段,得到的结果是一个列表套数据对象,该内只含有括号内指定的字段属性

对象点该字段属性是不会走数据库的,但是你一旦点了非括号内的字段,也能够拿到数据,但是会走数据库查询

only将括号内的字段查询结果赋值给对象,对象点括号内的字段是不会走数据库查询的,超出括号内的字段,此时查询会走数据库。

res = models.Book.objects.only('title')  # 這些對象内部只有title屬性
# print(res)
for r in res:
    # print(r.title)
    print(r.price)

defer的作用:

括号内传字段,得到的结果是一个列表套数据对象,对象点查询defer括号内的字段属性,该字段属性会重复的走数据库,但是查询defer括号内没有的字段,就不会走数据库。

res = models.Book.objects.defer('title')
for r in res:
    print(r.title)

selected_related:

内部是连表操作,先将关系表全部连接起来,之后再一次性查询出来,封装到对象中,数据对象之后在获取任意表中数据的时候都不需要再走数据库了,因为全部封装成了对象的属性。

select_related括号内只能传外键字段,并且不能是多对多字段,只能是一对多和一对一

select_related(外键字段1__外键字段2__外键字段3.....)

res = models.Book.objects.select_related('authors')
for r in res:
    print(r.publish.name)
    print(r.publish.addr)

prefetch_related

看起来像是连表操作,但内部是子查询,内部通过子查询将外键关联表中的数据全部封装到对象中,之后点当前对象表或外键关联表中的字段也都不需要走数据库了。

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

优缺点比较:

select_related连表操作,好处在于只走一次sql查询,但缺点是耗时耗在 连接表的操作上。

prefetch_related子查询,走两次sql查询,耗时耗在查询次数上。

django orm如何开启事务操作

事务介绍

事务的四大特性(ACID):

原子性:一个事务是一个不可分割的工作单位,事务中的所有操作都执行成功,整个事务才算成功;只要有任何一个sql语句执行失败,那么就会回退到执行事务前的状态。

一致性:事务前后数据的完整性必须保持一致,例如银行转账,甲方转给乙方钱,不管事务成功还是失败,应该保证事务结束后,两者的存款总额为2000元。

隔离性:指的是在并发环境中,当不同的事务同时操作同一个数据时,每个事务都有各自的完整数据空间

持久性:指的是只要事务成功结束,它对数据库中数据的改变就是永久性的。

# django orm开启事务操作
    from django.db import transaction
    with transaction.atomic():
        # 在with代码块中执行的orm语句同属于一个事务
        pass
 
    # 代码块运行结束 事务就结束了  事务相关的其他配置 你可以百度搜搜看

数据库的三大设计范式:

第一范式:要求属性具有原子性,不可以再分解;

不符合第一范式示例:

第二范式:2NF是对记录的唯一性,要求记录有唯一标识,即实体的唯一性,即不存在部分依赖。

表:学号、课程号、姓名、学分;

这个表明显说明了两个事务:学生信息, 课程信息;由于非主键字段必须依赖主键,这里学分依赖课程号姓名依赖与学号,所以不符合二范式。

可能会存在问题:

  • 数据冗余:,每条记录都含有相同信息;
  • 删除异常:删除所有学生成绩,就把课程信息全删除了;
  • 插入异常:学生未选课,无法记录进数据库;
  • 更新异常:调整课程学分,所有行都调整。

正确做法:
学生:Student(学号, 姓名);
课程:Course(课程号, 学分);
选课关系:StudentCourse(学号, 课程号, 成绩)。

第三范式:3NF是对字段的冗余性,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖;

表: 学号, 姓名, 年龄, 学院名称, 学院电话

因为存在依赖传递: (学号) → (学生)→(所在学院) → (学院电话) 。

可能会存在问题:

  • 数据冗余:有重复值;
  • 更新异常:有重复的冗余信息,修改时需要同时修改多条记录,否则会出现数据不一致的情况

正确做法:

学生:(学号, 姓名, 年龄, 所在学院);

学院:(学院, 电话)。

范式化设计和反范式化设计的优缺点:

范式化

优点:

  • 可以尽量的减少数据冗余,数据表更新快体积小
  • 范式化的更新操作比反范式化更快
  • 范式化的表通常比反范式化更小

缺点:

  • 对于查询需要对多个表进行关联,导致性能降低
  • 更难进行索引优化

反范式化

优点:

  • 可以减少表的关联
  • 可以更好的进行索引优化

缺点:

  • 存在数据冗余及数据维护异常
  • 对数据的修改需要更多的成本

django开启事务的方法

# django orm开启事务操作
from django.db import transaction
with transaction.atomic():
    # 在with代码块中执行的orm语句同属于一个事务
    pass
 
# 代码块运行结束 事务就结束了  事务相关的其他配置 你可以百度搜搜看

MTV与MVC模型

MTV django号称是MTV框架
M:models
T:templates
V:views
MVC
M:models
V:views
C:contronnar 控制器(路由分发 urls.py)
本质:MTV本质也是MVC

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!