Django之模型层

目录

一、前言

【1】测试脚本

【2】数据准备

二、单表操作

【1】数据的增加

【2】数据的删除

【3】数据的更改

三、常见的十几种查询方法

四、查看内部sql语句的方式

【1】方式一

【2】方式二

五、基于双下划线的查询

六、多表查询引入

【1】数据准备

【2】外键的增删改查

(1)一对多外键的增删改查

(2)多对多外键的增删改查

【补充】正反向的概念

七、多表查询

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

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

八、聚合查询

九、分组查询

十、F与Q查询

【1】F查询

【2】Q查询

十一、Django中如何开启事务

【1】ACID是数据库事务的四个关键特性

【2】Django中如何开启事务

十二、ORM中常用的字段及参数

【1】ORM字段

【2】ORM字段参数

【3】ORM关系字段

【4】自定义字段(了解)


一、前言

Django自带的sqlite3数据对日期格式不敏感,处理的时候容易出错

【1】测试脚本

  • 测试脚本
    • 当我们只想要测试Django中的某一个py文件的内容时,我们可以不需要书写前后端交互的形式,而是直接写一个测试脚本即可
  • 测试环境的准备
    • 在测试文件中书写下面内容
  • 这内容其实就是最外部 manage.py 文件中的上面几句话
    • 脚本代码无论是写在应用下的 tests.py文件还是自己新建文件,将内容写在新文件中,都会生效
python 复制代码
from django.test import TestCase
 
# Create your tests here.
import os
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day07.settings")
 
    import django
    django.setup()
    
    # 在下面书写我们需要测试的代码
    # 即所有的测试代码都必须等待环境准备完毕之后才能书写

【2】数据准备

  • 在models里面创建我们需要的数据库中的表
python 复制代码
from django.db import models
 
 
# Create your models here.
 
class User(models.Model):
    name = models.CharField(verbose_name="姓名", help_text="姓名", max_length=32)
    age = models.IntegerField(verbose_name="年龄", help_text="年龄")
    register_time = models.DateTimeField(verbose_name="注册时间", help_text="注册时间")
    '''
    DateField
    DateTimeField
        两个关键参数
            auto_now : 每次操作数据的时候该字段会自动将当前时间更新
            auto_now_add : 在创建数据的时候会自动将当前创建时间记录下来,只要不是人为修改,就不会发生更改
    '''
python 复制代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        # 数据库名字
        'NAME': 'django_day10',
        # 用户
        "USER": "root",
        # 密码
        "PASSWORD": "123",
        # IP
        "HOST": "127.0.0.1",
        # 端口
        "PORT": 3306,
        # 编码集
        "CHARSET": "utf8",
    }
}
  • 在项目下的 init.py 中声明数据库类型
python 复制代码
import pymysql
 
pymysql.install_as_MySQLdb()

二、单表操作

【1】数据的增加

python 复制代码
from django.test import TestCase
 
# Create your tests here.
import os
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day07.settings")
 
    import django
 
    django.setup()
 
    # 在下面书写我们需要测试的代码
    # 即所有的测试代码都必须等待环境准备完毕之后才能书写
    from app01 import models
 
    # 增加数据
    # register_time (1)支持自己指定传值
    res = models.User.objects.create(name="jerry", age=20, register_time='2023-11-15')
    # 返回值为对象本身
    print(res)  # User object
    
    # register_time (2)支持传入日期对象
    import datetime
    # 生成一个当前的时间对象
    c_time = datetime.datetime.now()
    user_obj = models.User.objects.create(name="egon", age=20, register_time=c_time)
    # 保存数据
    user_obj.save()

【2】数据的删除

python 复制代码
# 删除数据
"""pk : 自动查找到当前表的主键字段,指代的就是当前表的主键字段
使用 pk 后不需要知道当前表的主键字段,它会自动帮我们查找并匹配"""
# (1)方式一
res = models.User.objects.filter(pk=4).delete()
print(res) # (1, {'app01.User': 1})
# (2)方式二
# 拿到当前用户对象
user_obj = models.User.objects.filter(pk=1).first()
# 利用对象的方法进行删除
user_obj.delete()

【3】数据的更改

python 复制代码
# 数据的更改
# (1)方式一
res = models.User.objects.filter(pk=5).update(name="mengmeng")
print(res)  # 1
# (2)方式二
"""get方法返回的直接就是当前数据对象;
但是该方法不推荐使用:如果查询的数据不存在会直接报错 ,fileter不会"""
user_onj = models.User.objects.get(pk=5)
# 调用对象更改数据
user_onj.name = "xiaomeng"
user_onj.save()

三、常见的十几种查询方法

python 复制代码
    ################必知必会十三条################
    # (1) all() - 查询所有数据
    # (2) filter() - 带有过滤条件的查询
    # (3) get() - 根据条件拿数据对象,但是条件不存在会直接抛出异常
    # (4) first() - 拿queryset中的第一个元素
    res = models.User.objects.all().first()
    print(res)
    # (5) last() - 拿queryset中的最后一个元素
    res = models.User.objects.all().last()
    print(res)
 
    # (6) values() - 可以指定获取的数据字段
    res = models.User.objects.values('name')
    # 返回的数据格式为列表套字典 - 本质上是一个 QuerySet 对象 ,而不是真的列表
    print(res)  # <QuerySet [{'name': 'xiaomeng'}]>
    # (7) values_list() - 可以指定获取的数据字段
    res_list = models.User.objects.values_list('name', 'age')
    # 返回的数据格式为列表套元祖 - 本质上是一个 QuerySet 对象 ,而不是真的列表
    print(res_list)  # <QuerySet [('xiaomeng', 18)]>
    # 该语句可以查看当前执行命令的 SQL 语句 - 只有queryset对象才能使用该方法
    print(res_list.query)  
    # SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user`
 
    # (8) distinct() - 去重(带有主键就意味着数据存在不一样的地方,
    # 所以一定要去除主键后再去重;去重一定要是一模一样的数据)
    res = models.User.objects.values('name', 'age').distinct()
    print(res)
    # (9) order_by() - 排序
    # 默认升序
    res = models.User.objects.order_by('age')
    print(res)
    # 降序
    res = models.User.objects.order_by('-age')
    print(res)
 
    # (10) reverse() - 反转的前提是数据已经经过排序过的数据
    # 只能对有序的数据进行反转
    res = models.User.objects.order_by('age').reverse()
    print(res)
 
    # (11) count() - 统计当前数据的个数
    res = models.User.objects.count()
    print(res)
 
    # (12) exclude() - 排出在外
    # 将某个数据排出在结果之外
    res = models.User.objects.exclude(user="dream")
 
    # (13) exists() - 是否存在 - 返回布尔值 - 用处不大,因为数据本身就有布尔值的状态
    res = models.User.objects.filter(pk=5).exists()
    print(res)

四、查看内部sql语句的方式

【1】方式一

只有queryset对象才能使用该方法

python 复制代码
res_list = models.User.objects.values_list('name', 'age')
    # 返回的数据格式为列表套元祖 - 本质上是一个 QuerySet 对象 ,而不是真的列表
    print(res_list)  # <QuerySet [('xiaomeng', 18)]>
    # 该语句可以查看当前执行命令的 SQL 语句 - 只有queryset对象才能使用该方法
    print(res_list.query)  
    # SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user`

【2】方式二

所有 SQL语句 都可以使用

  • 在项目的settings.py文件中增加默认配置
python 复制代码
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

五、基于双下划线的查询

python 复制代码
    # 神奇的双下划线查询
    # (1)年龄大于35岁的数据
    res = models.User.objects.filter(age__gt=35)
    print(res)
    # (2)年龄小于35岁的数据
    res = models.User.objects.filter(age__lt=35)
    print(res)
    # (3)年龄大于等于32岁的数据
    res = models.User.objects.filter(age__gte=32)
    print(res)
    # (4)年龄小于等于30岁的数据
    res = models.User.objects.filter(age__lte=30)
    print(res)
    # (5)年龄是18或者32或者40
    res = models.User.objects.filter(age__in=(18, 32, 40))
    print(res)
    # (6)年龄是18-40之间 - 首尾都要
    res = models.User.objects.filter(age__range=(18, 40))
    print(res)
    # (7)查询出名字中含有 s 的数据  -- 模糊查询
    res = models.User.objects.filter(name__contains='s')
    print(res)
    # (7.1)是否区分大小写?
    res = models.User.objects.filter(name__contains='N')
    print(res)
    # 默认区分大小写
    # (7.2)忽略大小写
    res = models.User.objects.filter(name__icontains='N')
    print(res)
    # (8)以什么开头/结尾
    res = models.User.objects.filter(name__startswith='d')
    print(res)
    res = models.User.objects.filter(name__endswith='m')
    print(res)
    # (9)查询出注册时间是2020年1月份的数据/年/月/日
    res = models.User.objects.filter(register_time__month='1')
    print(res)
    res = models.User.objects.filter(register_time__year='2020')
    print(res)
    res = models.User.objects.filter(register_time__day='28')
    print(res)

六、多表查询引入

【1】数据准备

python 复制代码
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')
    # 多对多
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
   
    # varchar(254)  该字段类型不是给models看的,而是给后面我们会学到的校验性组件看的

    def __str__(self):
        return self.name


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()  
    # 电话号码用BigIntegerField或者直接用CharField
    addr = models.CharField(max_length=64)
  • 重载数据库
python 复制代码
python36 manage.py makemigrations
 
python36 manage.py migrate

【2】外键的增删改查

(1)一对多外键的增删改查

1.1 外键的增加

python 复制代码
# (1)外键的增加 - 直接写实际字段
models.Book.objects.create(title="三国演义", price=1369.25, publish_id=1)
# (2)外键的增加 - 虚拟字段
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title="红楼梦", price=1569.25, publish=publish_obj)

1.2 外键的删除

python 复制代码
# (2)一对多的外键的删除
models.Publish.objects.filter(pk=1).delete()  # 级联删除

1.3 外键的修改

python 复制代码
#  - 直接写实际字段
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)

(2)多对多外键的增删改查

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

2.1 增加

python 复制代码
# (1)如何给书籍添加作者
book_obj = models.Book.objects.filter(pk=1).first()
# book_obj.authors - 这样我们就已经能操作第三张关系表了
# 书籍ID为1的书籍绑定了一个主键为1的作者
book_obj.authors.add(1)
# 可以传多个参数
book_obj.authors.add(2,3)
# 支持参数传对象 - 且支持多个对象
book_obj.authors.add(author_obj)

2.2 删除(remove)

支持多个参数/支持对象

python 复制代码
# (2)删除
book_obj = models.Book.objects.filter(pk=1).first()
# 支持多个参数 - 支持多个对象
book_obj.authors.remove(2)

2.3 更改(set)

先删除后增加

python 复制代码
# (3)修改
book_obj = models.Book.objects.filter(pk=1).first()
# 括号内必须给一个可迭代对象
# 把 1 删掉 替换成 2
book_obj.authors.set([1, 2]) # 括号内必须给一个可迭代对象
# 把原来都删掉 , 替换成 3
book_obj.authors.set([3]) # 括号内必须给一个可迭代对象
# 支持放对象
book_obj.authors.set([author_obj])

2.4 清空

python 复制代码
# (4) 清空
# 在第三张表中清除某一本书和作者的绑定关系
book_obj = models.Book.objects.filter(pk=1).first()
# clear:括号内不要加任何参数
book_obj.authors.clear()

【补充】正反向的概念

  • 正向

    • 外键字段在我手上,那么我查你就是正向
    • book >>>> 外键字段在书这边(正向) >>>> 出版社
  • 反向

    • 外键字段不在我手上,那么我查你就是反向
    • 出版社>>>> 外键字段在书这边(反向) >>>> book
  • 一对一和一对多的判断也是这样

正向查询按字段

反向查询按表明名(小写)

​ __set

​ ...

七、多表查询

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

python 复制代码
# (1)查询书籍主键为1的出版社
book_obj = models.Book.objects.filter(pk=1).first()
# 书查出版社 - 正向 - 按字段查
res = book_obj.publish
print(res)  # Publish object
print(res.name)  # 东方出版社
print(res.addr)  # 东方
 
# (2)查询书籍主键为2的作者
book_obj = models.Book.objects.filter(pk=1).first()
# 书查作者 - 正向查询按字段
res = book_obj.authors
print(res)  # app01.Author.None
# 列表中存放的是作者对象
print(res.all())  # <QuerySet [<Author: Author object>]>
 
# (3)查询作者 的 电话号码
author_obj = models.Author.objects.filter(name="dream").first()
# 作者查询作者详情 - 正向查询按字段
res = author_obj.author_detail
print(res)  # AuthDetail object
print(res.phone)  # 110
print(res.addr)  # 山东
 
'''
    在书写ORM语句的时候跟写SQL语句一样的
    不要企图一次性将ORM语句写完,如果比较复杂,需要写一些看一些
    正向 什么时候需要加 .all()
        当查询返回的结果是多个的时候就需要用 .all()
        当查询的结果只有一个的时候直接拿到数据对象
            book_obj.publish
            book_obj.authors.all()
            author_obj.author_detail
    '''
 
# (4)查询出版社是东方出版社出版的书
# 先拿到出版社对象
publish_obj = models.Publish.objects.filter(name="东方出版社").first()
# 出版社查书 - 主键字段在书 - 反向查询
res = publish_obj.book_set.all()
# publish_obj.book_set
# print(res) # app01.Book.None
# publish_obj.book_set.all()
print(res)  
# <QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>]>
 
# (5)查询作者是dream写过的书
# 先拿到作者对象
author_obj = models.Author.objects.filter(name="dream").first()
# 作者查书 - 主键在书 - 反向
res = author_obj.book_set.all()
print(res)  # <QuerySet [<Book: Book object>]>
 
# (6)查询手机号是 110的作者姓名
# 先拿到作者详情的对象
author_detail_obj = models.AuthDetail.objects.filter(phone=110).first()
# 详情查作者 - 主键在作者 - 反向
res = author_detail_obj.author
print(res)  # Author object
print(res.name)  # dream

基于对象

  • 反向查询的时候
    • 当你的查询结果可以有多个的时候 就必须加_set.all()
    • 当你的结果只有一个的时候 不需要加_set.all()

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

python 复制代码
# (1)查询dream的手机号和作者的姓名
# 正向:先查询到作者信息再 .value(需要查询信息的表__需要查询的字段,其他字段)
res = models.Author.objects.filter(name="dream").values('author_detail__phone', 'name')
print(res)  
# <QuerySet [{'author_detail__phone': 110, 'name': 'dream'}]>

# 反向:先拿到详情,再用作者详情关联作者表,通过 __字段的方法 过滤出我们想要的指定数据
res = models.AuthDetail.objects.filter(author__name="dream").values('phone', 'author__name')
# AuthDetail.objects.filter(author__name="dream")
print(res)  # <QuerySet [<AuthDetail: AuthDetail object>]>
# AuthDetail.objects.filter(author__name="dream").values('phone','author__name')
print(res)  # <QuerySet [{'phone': 110, 'author__name': 'dream'}]>
 
# (2)查询书籍主键ID为1的出版社名字和书的名字
# 正向:先过滤出书籍ID为1的书籍对象,再去关联出版者表,利用__字段取值
res = models.Book.objects.filter(pk=1).values('title', 'publish__name')
print(res)  
# <QuerySet [{'title': '三国演义', 'publish__name': '东方出版社'}]>

# 反向:先查询到指定出版社,再从出版社反向找到书籍名字
res = models.Publish.objects.filter(book__id=1).values('name', 'book__title')
print(res)  # <QuerySet [{'name': '东方出版社', 'book__title': '三国演义'}]>
 
# (3)查询书籍主键ID为1的作者姓名
# 先拿到 书籍主键ID为1的对象,再关联作者信息表,通过__字段取值
res = models.Book.objects.filter(pk=1).values('authors__name')
print(res)  # <QuerySet [{'authors__name': 'dream'}]>

# 反向 : 先拿到 书籍ID为1的作者数据再去取作者的名字
res = models.Author.objects.filter(book__id=1).values('name')
print(res)  # <QuerySet [{'name': 'dream'}]>
 
# 查询书籍主键是1的作者的手机号
# book author authordetail
res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
print(res)  # <QuerySet [{'authors__author_detail__phone': 110}]> 
'''
    只要掌握了正反向的概念
    以及双下划线查询
    就可以无限跨表
    '''

八、聚合查询

python 复制代码
# 聚合查询
    # 聚合查询通常情况下都是配合分组一起使用的
    '''
    只要是和数据库相关的模块
        基本上都在 django.db.models 里面
        如果这里面没有 那大概率可能在 django.db 里面
    '''
    from django.db.models import Max, Min, Sum, Count, Avg
 
    # (1)所有书的平均价格
    # 正常情况下,我们是需要先进行分组再进行聚合函数运算的
    # 但是Django给我们提供了一种方法 :aggregate--> 可以不分组进行某个字段的聚合函数
    res = models.Book.objects.aggregate(Avg('price'))
    print(res)  # {'price__avg': 1890.083333}
 
    # (2)一次性使用
    res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'), Sum('price'), Count('pk'))
    print(res) 
    # {'price__avg': 1890.083333, 'price__max': Decimal('5959.25'), 'price__min': Decimal('555.25'), 'price__sum': Decimal('11340.50'), 'pk__count': 6}

九、分组查询

python 复制代码
# 分组查询   annotate
'''
    MySQL中的分组查询
        分组之后只能获取到分组的依据,组内其他字段都无法获取
            严格模式中可以修改
            ONLY_FULL_GROUP_BY
            set global sql_mode='ONLY_FULL_GROUP_BY'
    '''
from django.db.models import Max, Min, Sum, Count, Avg
# (1)统计每一本书的作者个数
# models 后面跟的是什么,就是按什么分组
# res = models.Book.objects.annotate()
res = models.Book.objects.annotate(author_number=Count('authors')).values('title','author_number')
'''
author_number 是我们自己定义的字段,用来存储统计出来的每本书的作者个数
'''
# 等价于
# res = models.Book.objects.annotate(author_number=Count('authors__pk')).values('title','author_number')
# print(res) 
# <QuerySet [{'title': '三国演义', 'author_number': 1}, {'title': '红楼梦', 'author_number': 0}, {'title': '水浒传', 'author_number': 0}, {'title': '论语', 'author_number': 0}, {'title': '孙子兵法', 'author_number': 0}, {'title': '镇魂街', 'author_number': 0}]>
 
# (2)统计每个出版社最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res) 
# <QuerySet [{'name': '东方出版社', 'min_price': Decimal('555.25')}, {'name': '北方出版社', 'min_price': Decimal('888.25')}]>
 
# (3)统计不止一个作者的图书
# (3.1)先按照图书分组
# (3.2)过滤出不止一个作者的图书
# 我的数据有限,我统计的是大于0的作者的图书
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=0).values('title', 'author_num')                                                                             
'''
只要ORM语句得到的是 一个 queryset 对象
那么就可以继续无限制的调用封装 的方法
'''
print(res)  # <QuerySet [{'title': '三国演义', 'author_num': 1}]>
 
# (4)查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
print(res)  
# <QuerySet [{'name': 'dream', 'sum_price': Decimal('1369.25')}, {'name': 'hope', 'sum_price': None}, {'name': 'sad', 'sum_price': None}]>
 
'''
    如果想按照指定的字段分组该如何处理
    如果 annotate 前面没东西 则会按照 Book 分组 ,如果前面有参数 就会按照前面的参数进行分组 price
    models.Book.objects.values('price').annotate()
    机器上如果出现分组查询报错的情况
    需要修改数据库严格模式
'''

十、F与Q查询

【1】F查询

python 复制代码
from django.db.models import F
 
# (1)查出卖出数大于库存数的书籍
# F 查询 : 帮助我们直接获取到表中的某个字段对应的数据
res = models.Book.objects.filter(sales__gt=F('stock'))
print(res)  # <QuerySet [<Book: 水浒传>]>
 
# (2).将所有书籍的价格提升500块
res = models.Book.objects.update(price=F('price') + 500)
print(res)  # 6 - 影响到了 6 条数据
 
# (3)将所有书的名称后边加上爆款两个字
# 在操作字符串的时候,F查询不能够直接坐到字符串的拼接
from django.db.models.functions import Concat
from django.db.models import Value
 
res = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
print(res)
# models.Book.objects.update(title=F('title') + '爆款')  # 所有的名称会全部变成空白

【2】Q查询

python 复制代码
# F与Q查询
 
from django.db.models import Q
 
# (1)查询卖出数大于100或者价格小于500的书籍
 
# (1.1)直接使用 filter 查询数据,逗号隔开,里面放的参数是 and 关系
res = models.Book.objects.filter(sales__gt=100, price__lt=500)
print(res)  # <QuerySet []>
 
# (1.2)直接使用 Q 查询数据,逗号隔开,里面放的参数还是 and 关系
res = models.Book.objects.filter(Q(sales__gt=100), Q(price__lt=500))
print(res)  # <QuerySet []>
 
# (1.3)直接使用 Q 查询数据,逗号可以换成其他连接符达到效果
res = models.Book.objects.filter(Q(sales__gt=100) or Q(price__lt=500))
# 二者等价 (| :或关系) (~  : 取反 not 关系)
res = models.Book.objects.filter(Q(sales__gt=100) | Q(price__lt=500))
print(res)  
# <QuerySet [<Book: 三国演义爆款>, <Book: 水浒传爆款>, <Book: 论语爆款>, <Book: 孙子兵法爆款>]>
 
# (2) Q的高阶用法 能够将查询条件的左边也变成 字符串形式
# 产生 Q 对象
q = Q()
# 修改Q查询的默认连接条件
q.connector = 'or'
q.children.append('sales', 100)
q.children.append('stock', 600)
# filter 参数支持Q对象,默认还是 and 关系
res = models.Book.objects.filter(q)
print(res)

十一、Django中如何开启事务

【1】ACID是数据库事务的四个关键特性

  • 事务是MySQL数据库中的一个重要概念。
  • 事务的目的:为了保证多个SQL语句执行成功,或执行失败,前后保持一致,保证数据安全。
  • 它代表了原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
    • 原子性(Atomicity):
      • 事务被视为一个不可分割的原子操作单元。
      • 这意味着要么全部操作成功并永久保存,要么全部操作失败并回滚到事务开始前的状态,不存在部分成功或部分失败的情况。
    • 一致性(Consistency):
      • 事务在执行前后,数据库都必须保持一致状态。
      • 这意味着事务执行前后,数据库中的数据必须满足所有定义的完整性约束,例如列级别的约束、外键关系等。
    • 隔离性(Isolation):
      • 事务之间应该相互隔离,每个事务的执行应该与其他事务的执行相互独立,互不干扰。
      • 隔离性确保了多个事务可以并发执行,而不会产生不一致的结果。
    • 持久性(Durability):
      • 一旦事务成功提交后,其所做的修改将永久保存在数据库中,即使发生系统故障或重启,数据也能够恢复到提交后的状态。
      • 持久性通过将事务日志写入非易失性存储介质来实现,如硬盘驱动器或固态硬盘。

【2】Django中如何开启事务

python 复制代码
from django.db import transaction
 
try:
    with transaction.atomic():
            # SQL1
            # SQL2
            # SQL3
     """在with代码块内书写所有的orm操作
        写在同一个with语句的代码块都是属于同一个事务,要么同时成功,要么同时失败"""
except Exception as e:
     print(e) # 记录日志
     transaction.rollback()

十二、ORM中常用的字段及参数

【1】ORM字段

AutoField

  • int自增列,必须填入参数 primary_key=True。
  • 当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField

  • 一个整数类型
  • 范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存)

CharField

  • 字符类型
  • 必须提供max_length参数, max_length表示字符长度。

DateField

  • 日期字段
  • 日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField

  • 日期时间字段
  • 格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

常用的字段

python 复制代码
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True

SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767

BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

BooleanField(Field)
- 布尔值类型
True/False  1/0

name varchar(1024)
content text;

TextField(Field)
- 文本类型

FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = ""      上传文件的保存路径
storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = ""      上传文件的保存路径
storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

TimeField(DateTimeCheckMixin, Field)
- 时间格式      HH:MM[:ss[.uuuuuu]]

FloatField(Field)
- 浮点型

DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度

BinaryField(Field)
- 二进制类型

【2】ORM字段参数

null

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

unique

  • 如果设置为unique=True 则该字段在此表中必须是唯一的
python 复制代码
Foregin(unique = True) ---->  OneToOneField

db_index

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

default

  • 为该字段设置默认值。

auto_now_add

  • 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now

  • 配置上auto_now=True,每次更新数据记录的时候会更新该字段。

【3】ORM关系字段

Foreignkey

  • 外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
  • ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。

to

  • 设置要关联的表

to-field

  • 设置要关联的表的字段

related_name

  • 反向操作时,使用的字段名,用于代替原反向查询是的'表名_set'。

related_query_name

  • 反向查询操作时,使用的连接前缀,用于替换表名。

on_delete

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

models.CASCADE

  • 删除关联数据,与之关联也删除

models.DO_NOTHING

  • 删除关联数据,引发错误IntegrityError

models.PROTECT

  • 删除关联数据,引发错误ProtectedError

models.SET_NULL

  • 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

models.SET_DEFAULT

  • 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

models.SET

  • 删除关联数据,
    • a. 与之关联的值设置为指定值,设置:models.SET(值)
    • b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

db_constraint

  • 是否在数据库中创建外键约束,默认为True。

OneToOneField

一对一字段。

通常一对一字段用来扩展已有字段。

一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。

python 复制代码
class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')
    

class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()

to

  • 设置要关联的表。

to_field

  • 设置要关联的字段。

on_delete

  • 同ForeignKey字段。

ManyToManyField

用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系

to

  • 设置要关联的表

related_name

  • 同ForeignKey字段。

related_query_name

  • 同ForeignKey字段。

symmetrical

  • 仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。

举个例子:

python 复制代码
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")

此时,person对象就没有person_set属性。

python 复制代码
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)

此时,person对象现在就可以使用person_set属性进行反向查询。

through

  • 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
  • 但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。

through_fields

  • 设置关联的字段。

db_table

  • 默认创建第三张表时,数据库中表的名称。

【4】自定义字段(了解)

python 复制代码
from django.db import models
 
# Create your models here.
# Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
    '''
    自定义的char类型的字段类
    '''
    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):
        '''
        限定生成的数据库表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        '''
        return 'char(%s)'%self.max_length
# 应用上面自定义的char类型
class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    class_name = FixCharField(max_length=16)
    gender_choice = ((1,'男'),(2,'女'),(3,'保密'))
    gender = models.SmallIntegerField(choices=gender_choice,default=3)
相关推荐
龙哥说跨境23 分钟前
如何利用指纹浏览器爬虫绕过Cloudflare的防护?
服务器·网络·python·网络爬虫
monkey_meng23 分钟前
【Rust中的迭代器】
开发语言·后端·rust
余衫马26 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng30 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
小白学大数据38 分钟前
正则表达式在Kotlin中的应用:提取图片链接
开发语言·python·selenium·正则表达式·kotlin
flashman91140 分钟前
python在word中插入图片
python·microsoft·自动化·word
菜鸟的人工智能之路43 分钟前
桑基图在医学数据分析中的更复杂应用示例
python·数据分析·健康医疗
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
秃头佛爷3 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
深度学习lover4 小时前
<项目代码>YOLOv8 苹果腐烂识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·苹果腐烂识别