Loading

Django框架之模型层

【一】前言

  • django自带的sqlite3数据库对日期格式不敏感,处理的时候总是出错
  • 所以通常都不用自带的数据库

【1】测试脚本

  • django项目的每一个app下都有一个test.py文件,这是一个测试脚本
  • 当我们想要测试一个django的某一张模型表时,就可以通过这个测试脚本,直接测试,而不需要写前后端路由和视图的交互。
  • 当然,也可以不用app自带的测试脚本,可以自己创建一个,但是测试脚本的测试环境都需要我们自己配置
## test.py
from django.test import TestCase
import os

# Create your tests here.
if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'student.settings')
    import django
    django.setup()
    ## 以上就是测试环境配置的代码
    
    from app1 import models ## 这一句话时导入模型层,必须得写在配置好的测试环境之下,要不然就会报错
 

【2】数据准备

  • 在模型层创建好一张表
from django.db import models
import datetime

# Create your models here.
class Student(models.Model):
    username = models.CharField(max_length=15)
    password = models.CharField(max_length=15)
    reg_time = models.DateTimeField(default=datetime.datetime.now())
  • 数据库配置代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'student',
        'USER': 'root',
        'PASSWORD': '123456',
        'IP': '127.0.0.1',
        'PORT': 3306
    }
}

【二】单表操作

【1】增删改查

# 增
# 方法1 这个返回值是创建的对象本身
res = models.Student.objects.create(username='hqq', password='123123')
# 方法2 
user_obj = models.Student(username='hqqq',password='123123')
user_obj.save()
# 删
# 方法1 res 返回值是一个元组,元组的第一个值是删除的个数
res = models.Student.objects.filter(pk=6).delete()
# 方法2
user_obj = models.Student.objects.filter(pk=12).first()
user_obj.delete()
# 改
# 方法1 返回值是影响的行数
res = models.Student.objects.filter(pk=1).update(password='123123123123')
# 方法2 get取对象的方法容易报错,如果get里面的条件找不到对象的话就会报错
user_obj = models.Student.objects.get(pk=1)
user_obj.password='123123'
user_obj.save()
# 查
# 方法1
user_obj = models.Student.objects.filter(pk=1)
# 方法2 get在找不到对象的时候就会报错
user_obj2 = models.Student.objects.get(pk=7)

【2】必知必会13条

# all 查询所有数据 查询的结果类似于一个字典
res = models.Student.objects.all()
# fillter() 筛选出符合条件的所有数据,如果没有筛选出数据不会报错
res = models.Student.objects.filter(pk=1)
# get() 筛选出符合条件的唯一对象,如果返回多个对象就会报错,没有符合条件的对象也会报错
res = models.Student.objects.get(username='tom')
# first() 返回查询到的对象中的第一个对象
res = models.Student.objects.all().first()
# last() 返回查询到的对象中的第最后一个对象
res = models.Student.objects.all().last()
# values() 返回指定字段的数据 返回的结果是一个类似于列表套字典的形式
res = models.Student.objects.values('username','password')
# values_list() 返回指定字段的数据,返回的结果是一个类似于列表套元组的形式
res = models.Student.objects.values_list('username','password')
# distinct() 对查询出来的数据进行去重,注意查询的数据的字段不能有主键
res = models.Student.objects.values('username', 'password').distinct()
# order_by() 对查询出来的数据以指定的字段进行排序,在指定字段前面加上减号就是降序,默认是升序
res1 = models.Student.objects.order_by('pk')
res2 = models.Student.objects.order_by('-pk')
# reverse() 对数据进行反转,数据必须是经过排序的,要不然不生效
res = models.Student.objects.order_by('pk').reverse()
# count() 对数据进行统计,一共有多少条
res = models.Student.objects.filter(username='green').count()
# exclude() 筛选除了符合条件以外的数据
res = models.Student.objects.exclude(username='green')
# exists() 判断某条数据是否存在,返回值是一个布尔值
res = models.Student.objects.filter(pk=1).exists()

【3】神奇的双下划线查询

# 大于 gt
# 查询年龄大于23岁的数据
res = models.Student.objects.filter(age__gt=23)

# 小于 lt
# 查询年龄小于21岁的数据
res = models.Student.objects.filter(age__lt=23)
# 大于等于 gte
# 查询年龄大于等于22岁的数据
res = models.Student.objects.filter(age__gte=23)

# 小于等于 lte
# 查询年龄小于等于19岁的数据
res = models.Student.objects.filter(age__lte=19)
# 在...里 in
# 查询年龄在18 19 23 岁的数据
res = models.Student.objects.filter(age__in=[18, 19, 23])
# 在...之间 range
# 查询年龄在18到25岁之间的数据 顾头又顾尾
res = models.Student.objects.filter(age__range=[18, 25])
# 模糊查询,区分大小写 contains
# 查询出名字里面含有y的数据,它默认是区分大小写的
res = models.Student.objects.filter(username__contains='y')

# 模糊查询,不区分大小写 icontains
res = models.Student.objects.filter(username__icontains='Y')
# 查询名字以g开头的数据 startswith
res = models.Student.objects.filter(username__startswith='g')

# 不区分大小写 istartswith
res = models.Student.objects.filter(username__istartswith='G')
# 查询时间 __year __month __day __hour __minute __second
# 查询注册时间的月份是4月的
res = models.Student.objects.filter(reg_time__month=4)

【三】多表查询引入

【1】数据准备

  • 在django中,建立外键联系与mysql中不一样,djiango是先建立表再建立外键联系
# 书籍表
class Book(models.Model):
    title = models.CharField(max_length=15)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateField(auto_now_add=True)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    '''
    书籍和作者的关系是多对多,需要带三张表来维系两表之间的关系,
    在django中,只需要声名一个虚拟字段,再用ManyToManyField建立联系,就能自动建立第三张表
    
    书籍和出版社的关系是一对多,一个出版社可以出版多本书,外键建立多的一放,即Book表,使用ForeignKey建立联系
    '''

# 作者表
class Author(models.Model):
    name = models.CharField(max_length=15)
    age = models.IntegerField(default=18)
    detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
    ''' 
    作者表和作者详情表是一对一的关系,外键建立再任意一方都可以,但是建立再查询频率高的一方,即Author表
    '''

# 作者详情表
class AuthorDetail(models.Model):
    phone = models.CharField(max_length=11)
    addr = models.CharField(max_length=15)


# 出版社表
class Publish(models.Model):
    name = models.CharField(max_length=15)
    addr = models.CharField(max_length=15)
    email = models.EmailField()

【2】一对多关系外键的增删改查

# 给书籍表添加数据

# 方式1 直接传值
res = models.Book.objects.create(title='三国演义', price='99.99', publish_id=1)

# 方式2 通过虚拟字段(就是在建立外键关系时用的字段名),传入出版社表的对象
publish_obj = models.Publish.objects.filter(pk=2).first()
res = models.Book.objects.create(title='西游记', price=88.88, publish=publish_obj)

# 删除某个出版社
# 返回的对象是一个元组,元组的第一个参数是删除的数量,第二个对象是一个字典,字典的键某张表,值是某张表删除数据的数量
res = models.Publish.objects.filter(pk=1).delete()

# 修改书籍表中的出版社ID
# 方式一 直接修改
res = models.Book.objects.filter(pk=2).update(publish_id=3)

# 方式二 通过虚拟字段(就是在建立外键关系时用的字段名),传入出版社表的对象
publish_obj = models.Publish.objects.filter(pk=2).first()
res = models.Book.objects.filter(pk=3).update(publish=publish_obj)

【3】多对多关系外键的增删改查

  • 操作多对多关系的外键,就要操作第三张表,很明显不能再直接操作现有的表了,因为models.py里面并没有django自动创建的第三张表
  • 需要通过建立外键的那张表创建的对象的属性去操作,这个属性就是我们建立第三张表时的那个虚拟字段
  • 最后再点add,括号里面可以填数字或者对应作者表创建的对象
  • 就可以给这本书添加一个在作者表中id为所填数字的作者id

# 给书籍添加作者
# 方法1 先创建一个书籍对象,再点书籍对象的author,再点add方法
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.add(1)  #给该书添加一个在作者表中主键为1的作者
 
# 方法2 直接塞一个对象进去
book_obj = models.Book.objects.filter(pk=2).first()
author_obj = models.Author.objects.filter(pk=1).first() # 创建作者对象
book_obj.author.add(author_obj)

# 给书籍删除作者
# 方法1 先创建一个书籍对象,再点书籍对象的author,再点remove方法
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.author.remove(2) # 删除在作者表中主键为2的作者

# 方法2 直接删除对象
book_obj = models.Book.objects.filter(pk=1).first()
author_obj = models.Author.objects.filter(pk=3).first()
res = book_obj.author.remove(author_obj)

# 修改书籍作者
# 修改的本质是先删除,再添加.需要使用set方法,set方法的参数必须是一个可迭代对象,如元组或者列表
# 方法1
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.set([1, 2, 3])

# 方法2 塞对象
book_obj = models.Book.objects.filter(pk=1).first()
author_obj_1 = models.Author.objects.filter(pk=1).first()
author_obj_2 = models.Author.objects.filter(pk=2).first()
book_obj.author.set([author_obj_1, author_obj_2])

清空

# 清空某本书和作者的所有关联
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.clear()

【4】正反向的概念

  • 现有书籍表和出版社表,是一对多的关系
  • 通过书籍表查询出版社,外键字段建立在书籍表,那么这就是正向查询
  • 通过出版社表查询书籍,外键字段建立在书籍表,那么这就是反向查询
  • 多对多和一对一的关系也是如此,外键建立在哪里,从那张表查询另一张表就是正向查询,反之就是反向查询。

【四】多表查询案例

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

  • 在写代码前,先确认这次查找是正向查找还是反向查找
  • 正向查找直接找字段名,反向查找直接找表名的小写
  • .all__set.all()是在前对象可能有多个后对象的情况下用的
# 1.查询书籍主键为1的出版社名称 正向查询找字段
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.publish.name
# 2.查询书籍主键为1的作者名称 正向查询找字段
book_obj = models.Book.objects.filter(pk=2).first()
author = book_obj.author.all().first().name # 由于这里一本书可能有多个作者,所以需要.all()
# 3.查询作者green的电话号码
author_obj = models.Author.objects.filter(name='green').first()
detail_obj = author_obj.detail.phone
# 4.查询出版社是东方出版社出版的书 反向查询找表名的小写
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all() # 由于一个出版社可能会对应很多本书所以这里需要加上_set.all()
# 5.查询作者是green的书 反向查询 找表名的小写
author_obj = models.Author.objects.filter(name='green').first()
res = author_obj.book_set.all() # 由于一个作者可能会有很多本书,所以这里需要加上_set.all()
# 6.查询手机号是110的作者姓名 反向查询 找表名的小写
author_detail_obj = models.AuthorDetail.objects.filter(phone='110').first()
author_obj = author_detail_obj.author.name # 由于这里作者详情和作者是一对一的关系,直接.表名的小写即可

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

  • 还是那句话正向查询找字段,反向查询找表名小写
  • filter和values都同样适用
# 1.查询green的手机号(一行代码解决) 正向查询找字段,反向查询找表名小写
# 正向查询
res = models.Author.objects.filter(name='green').values('detail__phone')

# 反向查询
res = models.AuthorDetail.objects.filter(author__name='green').values('phone')
# 2.查询书籍主键为1的出版社名称和书的名称
# 正向查询
res = models.Book.objects.filter(pk=1).values('publish__name','title')

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

# 反向查询
res = models.Author.objects.filter(book__pk=1).values('name')
# 4.查询green的手机号(一行代码解决) 不允许通过Author表查询
# 正向查询
res = models.Author.objects.filter(name='green').values('detail__phone')

# 反向查询
res = models.AuthorDetail.objects.filter(author__name='green').values('phone')
# 5.查询书籍主键是1的作者的手机号
# 正向查询
res = models.Book.objects.filter(pk=1).values('author__detail__phone')

# 反向查询
res = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')

【五】聚合查询

【1】引入

  • 聚合查询通常是需要配合分组一起使用的
  • 如果想要单独使用聚合查询就需要用到方法aggregate
  • 在django中使用聚合查询,需要从django.db.models导入方法from django.db.models import Sum, Max, Min, Count, Avg

【2】案例

# 查询所有书的平均价格
res = models.Book.objects.aggregate(Avg('price'))

# 聚合方法可以一次使用多个
res = models.Book.objects.aggregate(Avg('price'), Sum('price'), Max('price'), Min('price'), Count('pk'))

【六】分组查询

【1】引入

  • 在mysql中的分组查询,分组之后只能获取到分组的数据,其他字段无法获取。
  • 这是因为默认开启了严格模式

【2】语法

  • 在django中,分组的语法是models.表名.objects.annotate(),这时是以models.后面的表名为分组依据

  • 如果想要以某张表的字段为分组依据models.表名.objects.values('字段名').annotate()

  • 总结:如果有values则以values里面的字段为分组依据,如果没有则以models.后面的表名为分组依据

【3】案例

# 1.统计每个出版社买的书的最便宜的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')

# 分析
读题可知以出版社为分组依据,再利用聚合函数,对每个出版社出版的书的价格进行处理,得到价格最便宜的书
# 2.统计每一本书的作者个数
res =       models.Book.objects.annotate(author_num=Count('author__pk')).values('title','author_num')

# 分析
读题可知以书籍为分组依据,再利用聚合函数Count,先跳到author表中,去计算主键的个数。
# 3.统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('author__pk')).filter(author_num__gte=2).values('title','author_num')

# 解析
读题可知以书籍分组,先通过聚合函数Count,跳到author表中,去计算主键的个数得到每本书的作者的个数,再通过filter筛选author_num大于等于2的
# 4.查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')

# 解析
读题可知以作者分组,在分组内先跳到书籍表,利用Sum聚合函数获取书籍总价格

【七】F和Q查询

【1】F查询

  • 使用F查询前需要导入F类from django.db.models import F
  • F查询可以帮助我们直接获取到表中的某个字段的值
# 1.查询卖出数大于库存数的书籍
res = models.Book.objects.filter(maichu__gt=F('kucun'))

# 解析
如果不用F查询,可以发现一个尴尬的点,就是没办法表示出maichu__gt= 等号后面的数字,但是用了F查询,就可以很轻松的拿到某个字段对应的数据
# 2.将所有书籍的价格提升50块
res = models.Book.objects.all().update(price=F('price') + 50)
# 3.将所有数的名称后面加上爆款两个字
# 如果需要进行字符串拼接操作就需要导入其他两个类
'''
    # from django.db.models.functions import Concat
    # from django.db.models import Value
'''

res = models.Book.objects.all().update(title=Concat(F('title'), Value('爆款')))

【2】Q查询

  • 使用Q查询前需要导入Q类from django.db.models import Q
  • Q查询可以方便的改变查询条件之间是or 还是 and 还是 not
  • 两个Q对象之间用逗号连接是and关系 |连接是or关系 ~连接是not关系
# 1.查询卖出数大于100或者价格小于80的书籍
res = models.Book.objects.all().filter(Q(maichu__gt=100)|Q(price__lt=80))
# Q的高阶用法 能够将查询条件的左边也变成 字符串形式
q = Q()
q.connector = 'or'
q.children.append(('maichu__gt', 100))
q.children.append(('price__lt', 80))
res = models.Book.objects.all().filter(q)
print(res)

【八】Django中如何开启事务

【1】事务的四大特性ACID

# 原子性(Atomicity)
事务被视为一个不可分割的原子操作单元。
这意味着要么全部操作成功并永久保存,要么全部操作失败并回滚到事务开始前的状态,不存在部分成功或部分失败的情况

# 一致性(Consistency)
事务在执行前后,数据库都必须保持一致状态。
这意味着事务执行前后,数据库中的数据必须满足所有定义的完整性约束,例如列级别的约束、外键关系等。

# 隔离性(Isolation)
事务之间应该相互隔离,每个事务的执行应该与其他事务的执行相互独立,互不干扰。
隔离性确保了多个事务可以并发执行,而不会产生不一致的结果。

# 持久性(Durability)
一旦事务成功提交后,其所做的修改将永久保存在数据库中,即使发生系统故障或重启,数据也能够恢复到提交后的状态。
持久性通过将事务日志写入非易失性存储介质来实现,如硬盘驱动器或固态硬盘。

【2】Django中如何开启事务

# 首先要导入方法
# from django.db import transaction
## 开启事务
with transaction.atomic():
    ...

【九】ORM中常用的字段及参数

【1】常用字段

# 1.AutoField
int自增列,必须填入参数primary_key=True。一张表没有自增列时,会自动创建一个名为id的字段

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

# 3.CharField
字符类型,必须提供max_length参数,它表示字符串的最长长度

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

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

【2】字段合集

  1. 基础字段
    • CharField(max_length=None): 用于存储短文本字符串,指定最大长度。
    • TextField(): 用于存储长文本字符串。
    • IntegerField(): 用于存储整数。
    • FloatField(): 用于存储浮点数。
    • BooleanField(): 用于存储布尔值(True 或 False)。
    • DateField(): 用于存储日期。
    • DateTimeField(): 用于存储日期和时间。
    • TimeField(): 用于存储时间。
    • EmailField(): 用于存储电子邮件地址。
    • URLField(): 用于存储 URL。
    • UUIDField(): 用于存储 UUID。
    • FileField(): 用于上传文件。
    • ImageField(): 用于上传图像文件。
  2. 关系字段
    • ForeignKey(to, on_delete=None): 用于定义一对多关系,指向另一个模型的主键字段。
    • OneToOneField(to, on_delete=None): 用于定义一对一关系,每个源记录只能与一个目标记录关联。
    • ManyToManyField(to, ...): 用于定义多对多关系,允许一个模型的多个实例与另一个模型的多个实例相关联。
  3. 特殊字段
    • AutoField(): 自动增长的整数字段。
    • BigIntegerField(): 用于存储大整数。
    • BinaryField(): 用于存储二进制数据。
    • DecimalField(max_digits=None, decimal_places=None): 用于存储十进制数。
    • DurationField(): 用于存储时间间隔。
    • IPAddressField(): 用于存储 IPv4 地址。
    • GenericIPAddressField(): 用于存储 IPv4 或 IPv6 地址。
    • PositiveIntegerField(): 用于存储正整数。
    • PositiveSmallIntegerField(): 用于存储小范围的正整数。
    • SlugField(): 用于存储 URL 中的部分。
    • SmallIntegerField(): 用于存储小整数。
  4. 特殊约束
    • UniqueConstraint(fields, name=None): 确保字段组合的唯一性。
    • CheckConstraint(check, name=None): 添加自定义的检查约束条件。

【3】django和mysql字段之间的对应关系

'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',

【4】自定义字段

  • 自定义字段需要自己写一个类,继承models.Field类
  • 重写__init__db_type方法
class MyCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        super().__init__(max_length=max_length, *args, **kwargs)
        self.max_length = max_length

    def db_type(self, connection):
        # 返回真正的数据以及各种约束条件
        return f'CHAR({self.max_length})'

【十】数据库查询优化

【1】惰性查询

  • ORM语句的特点是惰性查询
  • 如果我们只是写了一条ORM语句,但是并没有用到这条语句锁查出来的数据时,django是不会执行这条ORM语句的。
# 不执行的情况
models.Book.objects.all()

# 执行的情况
res = models.Book.objects.all()
print(res)

【2】only与defer

  • only和defer是django中控制查询返回字段的方法

  • only和defer括号里面的参数都是一个或者多个字段

  • only方法用于指定查询结果中应该包含的字段,用该方法得到的对象,点括号内的字段可以不用再经过数据库直接拿到数据

  • defer方法用于指定查询结果中不应该包含的字段,用该方法得到的对象,点括号内的字段需要重新再从数据库拿数据。

# only
res = models.Book.objects.all()
for i in res:
    print(res.get('title')) 

# defer
res = models.Book.objects.defer('title')
for s in res:
  print(res.price)
  • select_related和prefetch_related是django中用于优化数据库查询的两种方法
  • select_related内部对应的是数据库的联表查询,它的参数只能填一对一或者一对多的外键字段
  • prefetch_related内部对应的是数据库的子查询,它的参数可以填所有关系的外键字段
  • 用这两个方法创建出来的对象,封装好了本表与外键关联的表的所有数据,通过他们查询数据将不再需要经过数据库,从而优化了查询效率
# prefetch_related
res = models.Book.objects.prefetch_related('publish')
for i in res:
    print(i.publish.name)
    
# select_related
res = models.Book.objects.select_related('publish') # INNER JOIN
for i in res:
    print(i.publish.name)

【十一】(数据库字段设计常见)

【1】引入

  • 在Django中,字段属性choices允许您为模型字段指定一组预定义选项。这通常用于限制字段的值,使其仅限于预定义的一组选项之一。

  • choices属性通常与CharFieldIntegerField等字段一起使用,但它也可以与其他字段类型一起使用

  • 以一张用户表为例,它有名称,年龄,性别,生育情况这些字段

  • 其中,性别和生育情况的对应数据是很有限的,这时候就可以使用choices参数

【2】数据准备

class User(models.Model):
    name = models.CharField(max_length=15)
    age = models.IntegerField()
    gender_choices = (
        (1, 'male'),
        (2, 'female'),
        (3, 'other')
    )
    birth_choices = (
        ('1', '是'),
        ('2', '否'),
        ('3', '未知'),
    )
    gender = models.IntegerField(choices=gender_choices)
    birth = models.CharField(choices=birth_choices, max_length=15)

【3】使用

  • 对于定义了choices属性的字段来说,可以通过obj.get_字段名_display
  • 如果对应字段的值在自定义的choices内,就可以拿到对应的值
  • 如果不在自定义的范围内,就会拿到它原本的值
# 1.插入数据,其中gender和birth的参数在预定义选项之内,并且查询
models.User.objects.create(name='green', age=18, gender=1, birth='1')
models.User.objects.create(name='green', age=18, gender=1, birth='1')
obj = models.User.objects.filter(pk=1).first()

print(obj.gender) # 1
print(obj.birth)  # 2
print(obj.get_gender_display()) # male
print(obj.get_birth_display()) # 否

# 2.插入数据,其中gender和birth的参数不在预定义选项之内,并且查询
models.User.objects.create(name='green', age=18, gender=5, birth='5')
obj = models.User.objects.filter(pk=2).first()

print(obj.gender) # 5
print(obj.birth)  # 5
print(obj.get_gender_display()) # 5
print(obj.get_birth_display()) # 5

【十二】MTV和MVC模型

  • MTV(Model-Template-View)和MVC(Model-View-Controller)都是用于设计和组织Web应用程序的模式。
  • 它们都旨在将应用程序的不同组成部分分离开来,以便更好地管理代码、提高可维护性和重用性。
  • django框架就是MVC模型

【1】MVC模型

  • Model(模型):负责处理应用程序的数据逻辑。它通常包含与数据库交互的代码,以及数据的验证和处理。
  • View(视图):负责用户界面的呈现。它将数据从模型中获取,并将其呈现给用户。视图通常是用户与之交互的界面元素,如HTML页面、模板等。
  • Controller(控制器):负责协调模型和视图之间的交互。它接收用户的输入,处理请求,并相应地更新模型和视图。控制器通常包含应用程序的业务逻辑。

【2】MTV模型

  • Model(模型):与MVC中的模型类似,负责处理应用程序的数据逻辑,包括数据的存储、验证和处理。
  • Template(模板):与MVC中的视图类似,负责用户界面的呈现。模板将数据从模型中获取,并将其呈现给用户。模板通常包含HTML和模板语言代码。
  • View(视图):与MVC中的控制器类似,负责协调模型和模板之间的交互。它接收用户的请求,处理请求,并相应地更新模型和模板。视图通常包含应用程序的业务逻辑。

【十三】创建多对多外键关系的三种方法

  • 在django中创建多对多外键的方法有三种,可以把他们总结为全自动,半自动,和纯手动
  • 最推荐的是半自动,因为它支持orm的正向查询和反向查询,第三张表还有很强的拓展性

【1】全自动

  • 通过django强大的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)

【2】半自动(推荐)

  • 半自动也是需要我们自己去定义第三张表,虽然要多写代码,但是也能自定义关系表,有很强的拓展性
  • 同时它也需要用ManyToManyField定义外键,里面的还要多两个属性through和through_fields
  • 用半自动的方法,不能再使用add,remove,set,remove等方法,但是可以直接去操作第三张表,也是挺方便的
class Book(models.Model):
    name = models.CharField(max_length=32)
    # 全自动
    # through_fields : 当前表是谁,第一个参数就是谁
    # 判断的本质:通过第三张表查询对应的表,需要用到哪个字段就把哪个字段放在前面
    authors = models.ManyToManyField(to='Author', through='BookAuthor', through_fields=('book', 'author'))


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


class BookAuthor(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')

【3】纯手动

  • 这种方法一般不推荐,因为它不仅代码量大,还不能使用orm提供的快捷方便的正反向查询语法
class Book(models.Model):
    name = models.CharField(max_length=32)

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

class BookAuthor(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')
posted @ 2024-03-25 17:29  HuangQiaoqi  阅读(2)  评论(0编辑  收藏  举报