Django模型基础

文章目录

  • 一、models字段类型
    • 概述
    • 属性命名限制
    • 使用方式
    • 逻辑删除和物理删除
    • 常用字段类型
  • 二、常用字段参数
    • 常用字段选项(通过字段选项,可以实现对字段的约束)
  • 实践
      • 创建模型
      • 执行迁移命令 并 创建超级用户登录admin后台
      • 添加文件和图片字段
      • 定义模型字段和约束及在Admin后台管理系统的表单情况
  • 三、models基本操作
    • 3.1 增
      • 实践
        • 方式一
        • 方式二
        • 方式三
        • 方式四(不会报错)
      • 测试
      • 添加多条数据
    • 3.2 删
      • 删除多条数据
    • 3.3 修改数据
      • 修改多条数据
    • 3.4 查
      • get() 获取单条数据
      • all() 获取所有数据
      • first() 返回第一条数据
      • last() 返回最后一条数据
      • filter() 过滤
      • count(): 返当前查询集中的对象个数
      • exists():判断查询集中是否有数据
      • values(): 获取指定列的值
      • values_list():获取指定列的值
      • exclude() 排除,取反
      • 聚合
      • 排序
      • 分页功能
        • 手动分页
        • Django中的自动分页器
  • 四、Django模型进阶
    • 4.1 配置MySQL
      • 实践
        • 报错1:django.db.utils.NotSupportedError: MySQL 8 or later is required (found 5.7.26).
        • 报错2:RuntimeWarning: Got an error checking a consistent migration history performed for database connection 'default': (1045, "Access denied for user 'root'@'localhost' (using password: YES)")
    • 4.2 多模块关联关系
    • 4.3 Model连表结构
      • 4.3.1 一对多关联
      • 实践
        • 增加数据
        • 删除数据
        • 修改数据
        • 查询数据
          • 前三种查询
          • 第四种查询
      • 4.3.2 多对多关联
      • 实践
        • 添加数据
          • 写法一
          • 写法二
        • 修改数据
        • 删除数据
          • 删除电影
          • 删除用户
        • 查询
      • 4.3.3 一对一
        • 实践
          • 增删改
          • 查询


一、models字段类型

概述

django根据属性的类型确定以下信息

  • 当前选择的数据库支持字段的类型
  • 渲染管理表单时使用的默认html控件
  • 在管理站点最低限度的验证
    django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列。

属性命名限制

  • 遵循标识符规则
  • 由于django的查询方式,不允许使用连续的下划线,写一个下划线是可以的,写两个下划线_不可以
    定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用被导入到django.db.models中

使用方式

  • 导入from django.db import models
  • 通过models.Field 创建字段类型的对象,赋值给属性

逻辑删除和物理删除

对于重要数据都做逻辑删除,不做物理删除,实现方法是定义is_delete属性,类型为BooleanField,默认值为False
is_delete = models.BooleanField(default=False)

常用字段类型

  • AutoField

    • 一个根据实际ID自动增长的IntegerField,通常不指定,如果不指定,主键字段id将自动添加到模型中
  • CharField(max_length=字符长度)

    • 字符串,默认的表单样式是 Input
  • DecimalField(max_digits=None, decimal_places=None)

    • 使用python的Decimal实例表示的十进制浮点数
    • 参数说明
      • DecimalField.max_digits
        • 位数总数
      • DecimalField.decimal_places
        • 小数点后的数字位数
  • FloatField

    • 用Python的float实例来表示的浮点数
  • BooleanField

    • True/False 字段,此字段的默认表单控制是CheckboxInput
  • DateField( [auto_now=False, auto_now_add=False] )

    • 使用Python的datetime.date实例表示的日期
    • 参数说明
      • DateField.auto_now
        • 每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改”的时间戳,它总是使用当前日期, 默认为false
      • DateField.auto_now_add
        • 当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用创建时的日期默认为false
          注意: auto_now_add,auto_now,and default 这些设置是相互排斥的 , 他们之间的任何组合将会发生错误的结果
  • TimeField

    • 使用Python的datetime.time实例表示的时间,参数同DateField
  • DateTimeField

    • 使用Python的datetime.datetime实例表示的日期和时间,参数同DateField
  • FileField

    • 一个上传文件的字段
  • ImageField

    • 继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image需要安装Pillow: "pip install Pillow"

二、常用字段参数

常用字段选项(通过字段选项,可以实现对字段的约束)

  1. null=True
    数据库中字段是否可以为空
  2. blank=True
    django的Admin 中添加数据时是否可允许空值

一般null=True & blank=True 搭配着用,出现null=True就用上blank=True

  1. primary_key = True
    主键,对AutoField设置主键后,就会代替原来的自增 id 列
  2. auto_now和 auto_now_add
    auto_now           自动创建 - - - 无论添加或修改,都是当前操作的时间
    auto_now_add   自动创建 - - - 永远是创建时的时间
  3. choices   (后台admin下拉菜单)
    USER_TYPE_LIST = (
       (1, “超级用户”),
       (2, ‘普通用户’),
    )
    user_type = models.IntegerField( choices=USER_TYPE_LIST, default=1, verbose_name=‘用户类型’)
  4. max_length 最大长度
  5. default 默认值
  6. verbose_name    Admin(后台显示的名称)中字段的显示名称
  7. name | db_column   数据库中的字段名称,在我做迁移的时候默认情况表当中的字段名称和写的属性名是一样的,如果想不一样就用name 或者 db_column 来指定
  8. unique=True   不允许重复
  9. db_index = True   数据库索引, 例如: 如果你想通过name查询的更快的话,给他设置为索引即可
  10. editable=True   在Admin里是否可编辑,不可编辑则不显示
  11. 设置表名
    class Meta:
      db_table = ‘person’

实践

新建一个项目Day03DjangoPro01
在这里插入图片描述

创建模型

App\models.py

from django.db import modelsclass UserModel(models.Model):# uid 它会成为主键,原来的id不会创建uid = models.AutoField(auto_created=True, primary_key=True)# CharField: 字符串类型      最大长度      unique唯一          db_index索引name = models.CharField(max_length=30, unique=True, db_index=True)# IntegerField: 整数类型, default默认值age = models.IntegerField(default=18)# BooleanField 类型sex = models.BooleanField(default=True)  # True男# TextField 大文本  null=True 可以为空  blank=True  在Admin管理页面可以为空info = models.TextField(null=True, blank=True)# FloatField 小数salary = models.FloatField(default=100000.456)# DecimalField 十进制小数  max_digits=9 总长度9 ,decimal_places=2 小数点后保留2位money = models.DecimalField(max_digits=9, decimal_places=2, default=0)# 日期birthday = models.DateField(default='2000-3-4')birthday2 = models.DateTimeField(auto_now=True)  # auto_now=True 每一次修改后都会自动修改该时间为最新的修改时间birthday3 = models.DateTimeField(auto_now_add=True)  # auto_now_add=True 第一次添加数据的时候的时间,以后不会修改

数据迁移

迁移的概念: 就是将模型映射到数据库的过程
生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

如果遇到 Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
Select an option:

1)现在提供一次性默认值(将在所有现有行上设置此列的空值)
2)退出并在models.py中手动定义一个默认值。

这里是告诉你: 因为新添加了字段,原有数据因为没有这个新字段,那么1就是设置一次性默认值填入到之前的数据中;2 models.py中手动定义一个默认值default=。

Select an option: 输入1或者2 回车

创建超级用户
python manage.py createsuperuser
用户名 admin
密码 123456
弹出信息,你的密码太简单了太短了,至少八个字符,看你是否要使用,Y就是的使用


执行迁移命令 并 创建超级用户登录admin后台

App\admin.py

from django.contrib import adminfrom App.models import *# 后台管理系统的使用:
# 在这里注册对应的模型
admin.site.register(UserModel)

打开cmd

workon envdjango4
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

访问admin后台: http://127.0.0.1:8000/admin/

在这里插入图片描述
在这里插入图片描述
添加一条数据,在数据库观察 birthday2 和 birthday3
在这里插入图片描述
在这里插入图片描述

修改刚刚添加的数据
在这里插入图片描述
可以看到 birthday2 发生了变化,birthday3 没有改变。

auto_now=True 每一次修改后都会自动修改该时间为最新的修改时间
auto_now_add=True 第一次添加数据的时候的时间,以后不会修改

添加文件和图片字段

App\models.py

# 既可以上传文件又可以上传图片
icon = models.FileField(null=True, blank=True, upload_to='static/uploads')  # upload_to 上传的地方

在这里插入图片描述
再重新生成迁移文件、执行迁移

python manage.py makemigrations
python manage.py migrate

执行完之后进入admin后台管理页面,点击添加
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


App\models.py

# 限制只能上传图片
icon2 = models.ImageField(null=True, blank=True, upload_to='static/uploads')

在这里插入图片描述
安装

# 1
python -m pip install Pillow
# 2
pip install Pillow
# 3 镜像
pip install pillow -i https://pypi.douban.com/simple

再重新生成迁移文件、执行迁移

python manage.py makemigrations
python manage.py migrate

执行完之后进入admin后台管理页面,点击添加

在这里插入图片描述

定义模型字段和约束及在Admin后台管理系统的表单情况

App\models.py

# 其他约束
choices = ((1, '青铜'), (2, '白银'), (3, '王者'))
# 保存到数据库当中的是前面的数字123,显示出来的是青铜白银王者,
#青铜白银王者在后台管理系统中会显示出来,实际上数据库存的是数字123
user_type = models.IntegerField(choices=choices, default=1,name='utype', verbose_name='用户类型')
user_type2 = models.IntegerField(default=1, editable=False,db_column='utype2', verbose_name='用户类型2')

再重新生成迁移文件、执行迁移

python manage.py makemigrations
python manage.py migrate

在这里插入图片描述

在这里插入图片描述
user_type在后台系统显示,user_type2不显示,是因为editable=False

三、models基本操作

一般的数据库操作流程:

  1. 创建数据库,设计表结构和字段
  2. 连接Mysql数据库,并编写数据访问层代码
  3. 业务逻辑层去调用数据访问层执行数据库操作

    Django通过Model操作数据库,不管你数据库的类型是MySql或者Sqlite,Django自动帮你生成相应数据库类型的SQL语句,所以不需要关注SQL语句和类型,对数据的操作Django帮我们自动完成。只要会写Model就可以了。
    django使用对象关系映射 (Object Relational Mapping,简称ORM) 框架去操控数据库。
    ORM(Object Relational Mapping) 对象关系映射,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。

3.1 增

# ORM:模型		<=>		表类结构	->		表结构对象		->		表的一条数据类属性	->		表的字段# models基本操作
# 增:1) 创建对象实例,然后调用save方法:obj = Author()obj.first_name = 'zhang'obj.last_name = 'san'obj.save()2)创建对象并初始化,再调用save方法:obj = Author(first_name='zhang',last_name='san')obj.save()3)使用create方法Author.objects.create(first_name='li',last_name='si')4)使用get_or_create方法,可以防止重复Author.objects.get_or_create(first_name='zhang', last_name='san')

实践

我们单独再创建一个模型吧

App\models.py

from django.db import models# 增删改查
class PersonModel(models.Model):name = models.CharField(max_length=30, unique=True)age = models.IntegerField(default=18)# 表名class Meta:db_table = 'person'

注意:我们的name设置的是unique唯一的, 后面实践会用到
生成迁移文件、执行迁移

python manage.py makemigrations
python manage.py migrate

在这里插入图片描述
就不写子路由了,根路由Day03DjangoPro01\urls.py

from django.contrib import admin
from django.urls import path
from App.views import *urlpatterns = [path('add/', add_person),  # 添加path('admin/', admin.site.urls),
]

方式一

App\views.py

from django.shortcuts import render, HttpResponsefrom App.models import *# 增加数据
def add_person(request):# 方式一try:p = PersonModel()p.name = '清风'p.age = 30p.save()  # 同步到数据库表中except Exception as e:return HttpResponse('add fail! 添加失败')return HttpResponse('add success! 添加成功')

方式二

App\views.py

def add_person(request):# 方式二try:p = PersonModel(name='微泫', age=26)p.save()  # 同步到数据库表中except Exception as e:return HttpResponse('add fail! 添加失败')return HttpResponse('add success! 添加成功')

方式三

App\views.py

def add_person(request):# 方式三try:PersonModel.objects.create(name='意境', age=26)except Exception as e:return HttpResponse('add fail! 添加失败')return HttpResponse('add success! 添加成功')

方式四(不会报错)

App\views.py

def add_person(request):# 方式四ret = PersonModel.objects.get_or_create(name='甘雨', age=26)print('ret: ', ret)# (<PersonModel: PersonModel object (3)>, False)  失败# (<PersonModel: PersonModel object (4)>, True) 成功print('type: ', type(ret))  # <class 'tuple'>print('Bool: ', ret[1])  # False 失败;True 成功if ret[1]:return HttpResponse('add success! 添加成功')return HttpResponse('add fail! 添加失败')

测试

http://127.0.0.1:8000/add/
第一次:
在这里插入图片描述

第二次:因为name我们设置的是唯一的,所以添加失败

在这里插入图片描述
在这里插入图片描述

添加多条数据

App\views.py

def add_person(request):# 添加多条数据for i in range(1, 10):PersonModel.objects.get_or_create(name=f'景{i}元', age=18 + i)return HttpResponse('add success! 添加成功')

http://127.0.0.1:8000/add/
在这里插入图片描述

3.2 删

使用Queryset的delete方法:# 删除指定条件的数据Author.objects.filter(first_name='zhang').delete()# 删除所有数据Author.objects.all().delete()注意: objects不能直接调用delete方法。使用模型对象的delete方法:obj = Author.objects.get(id=5)obj.delete()

根路由Day03DjangoPro01\urls.py

 path('del/', del_person),  # 删除

App\views.py

def del_person(request):# 删除数据# 1. 先找到要删除的数据# 2. 然后删除# 删除一条数据try:p = PersonModel.objects.first()  # 第一条数据p.delete()except Exception as e:return HttpResponse("删除失败!")return HttpResponse("删除成功!")

http://127.0.0.1:8000/del/
在这里插入图片描述

删除多条数据

def del_person(request):# 删除多条数据try:PersonModel.objects.filter(age__gt=23).delete()  # 条件年龄 age > 23 岁的except Exception as e:return HttpResponse("删除失败!")return HttpResponse("删除成功!")

3.3 修改数据

Author.objects,filter(last_name='dfdf').update(last_name='san')
模型没有定义update方法,直接给字段赋值,并调用save,能实现update的功能,比如:obj = Author.objects.get(id=3)obj.first_name ='zhang'obj.save()
save更新时会更新所有字段。如果只想更新某个字段,减少数据库操作,可以这么做:obj.first_name ='li'obj.save(update_fields=['first_name'])

Day03DjangoPro01\urls.py

path('update/', updete_person)

App\views.py

def updete_person(request):# 修改数据# 1. 先找到要修改的数据# 2. 然后修改try:# 修改一条数据p = PersonModel.objects.get(id=7)p.age = 110# p.save()  # 同步到数据库表中p.save(update_fields=['age'])  # 指定更新的字段,一定程度提高更新效率except Exception as e:return HttpResponse("修改失败!")return HttpResponse("修改成功!")

http://127.0.0.1:8000/update

在这里插入图片描述

修改多条数据

def updete_person(request):# 修改多条数据try:PersonModel.objects.all().update(age=66)except Exception as e:return HttpResponse("修改失败!")return HttpResponse("修改成功!")

PersonModel.objects.all() 是一个查询集

3.4 查

get(): 获取单条数据Author.objects.get(id=123)如果没有找到符合条件的对象,会引发模型类.DoesNotExist异常如果找到多个,会引发模型类.MultipleObjectsReturned 异常
first():返回查询集(QuerySet)中的第一个对象
last():返回查询集中的最后一个对象
count():返当前查询集中的对象个数
exists():判断查询集中是否有数据,如果有数据返回True没有反之
a11():获取全部数据:Author.objects.al1()
values(): 获取指定列的值,可以传多个参数!返回包含字典的列表(保存了字段名和对应的值)Author.objects.all().values('password')
values_list():获取指定列的值,可以传多个参数!返回包含元组列表 (只保存值)Author.objects.al1().values_list('password')进阶操作:#获取个数Author.objects.filter(name='seven').count()Author.objects.filter(id__gt=1)# 获取id大于1的值# select * from Author where id > 1Author.objects.filter(id__gte=1)# 获取id大于或等于1的值# select * from Author where id >= 1Author.objects.filter(id__lt=10)# 获取id小于10的值# select * from Author where id < 10Author.objects.filter(id__lte=10)# 获取id小于或等于10的值# select * from Author where id <= 10Author.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值# select * from Author where id < 10 and id > 1Author.objects.filter(id__in=[11, 22, 33] ) # 获id在11、22、33中的数据# select * from Author where id in (11,22,33)Author.objects.exclude(id__in=[11, 22, 33]) # not in# select * from Author where id not in (11,22,33)Author.objects,filter(name__contains="ven") # contains (和数据库中like语法相同)# select * from Author where name like '%ven%'Author.objects.filter(name__icontains="ven") # icontains大小写不敏感Author.objects.filter(name__regex="^ven") # 正则匹配Author.objects.filter(name__iregex="^ven") # 正则匹配,忽略大小写Author.objects.filter(age__range=[10, 20])# 范围bettwen and 10-20# xx__startswith , xx__istartswith , xx__endswith, xx__iendswith:# 以什么开始,以什么结束,和上面一样带i的是大小写不敏感的,其实不带i的也忽略大小写Author.objects.filter(name='seven').order_by('id')# asc升序Author.objects.filter(name='seven').order_by('-id')# desc降序Author.objects.a11()[10:20] # 切片,取所有数据的10条到20条,分页的时候用的到#下标从0开始,不能为负数,可以实现分页#手动分页page 页码per_page 每页数量 =51(page=1):0-4 =>[0:5]2(page=2):5-9 =>[5:10]3(page=3):10-14 =>[10:15]4(page=4):15-19 =>[15:20]...每一页数据范围:(page-1)*per_page: page*per_page
#聚合使用aggregate()函数返回聚合函数的值Avg:平均值Count:数量Max:最大Min:最小Sum:求和from django.db.models import Count, Min, Max, SumAuthor.objects.aggregate(Max('age'))

注意双下划线__

Day03DjangoPro01\urls.py

path('get/', get_person),  # 查询

get() 获取单条数据

如果没有找到符合条件的对象,会引发模型类.DoesNotExist异常
如果找到多个,会引发模型类.MultipleObjectsReturned 异常
App\views.py

# 查询数据
def get_person(request):# get(): 得到一个对象(一条数据)p = PersonModel.objects.get(id=8)print('*' * 52)print(p, type(p))print(p.name, p.age)print('*' * 52)return HttpResponse('查询成功')

http://127.0.0.1:8000/get/
在这里插入图片描述

加入我调用get() ‘’id=‘’不写会怎么样?

# p = PersonModel.objects.get(8) # 会报错

pk:primary key

p = PersonModel.objects.get(pk=8)  # pk:primary key

查询成功

注意1:get()只能返回一个对象, 不然会报错 MultipleObjectsReturned

p = PersonModel.objects.get(age=19)  # 数据库只有一个age=19,所以不会报错
p = PersonModel.objects.get(age=66)  # 数据库有很多age=66,所以报错 MultipleObjectsReturned

在这里插入图片描述

注意2:get() 查询结果不存在 DoesNotExist

 p = PersonModel.objects.get(age=1000)  # 数据库没有age=1000,所以报错 DoesNotExist

all() 获取所有数据

App\views.py

# 查询数据
def get_person(request):# 获取所有数据persons = PersonModel.objects.all()print(persons, type(persons))# QuerySet 查询集# 可以遍历查询集for p in persons:print(p.name, p.age)return HttpResponse('查询成功')
<QuerySet [<PersonModel: PersonModel object (7)>, <PersonModel: PersonModel object (8)>,<PersonModel: PersonModel object (9)>, <PersonModel: PersonModel object (10)>,<PersonModel: PersonModel object (11)>, <PersonModel: PersonModel object (16)>]> <class 'django.db.models.query.QuerySet'>
景1元 110
景2元 110
景3元 66
景4元 66
景5元 66
甘雨 19

类型:QuerySet 查询集

first() 返回第一条数据

返回queryset中匹配到的第一个对象,如果没有匹配到对象则为None,如果queryset没有定义排序,则按主键自动排序。

all[0]:
与 first() 不同,如果没有匹配到,会报IndexError错误。

App\views.py

def get_person(request):# 获取第一条数据p = PersonModel.objects.first()print(p.name, p.age)return HttpResponse('查询成功')

last() 返回最后一条数据

App\views.py

def get_person(request):# 获取最后一条数据p = PersonModel.objects.last()print(p.name, p.age)return HttpResponse('查询成功')

filter() 过滤

使用最多。类似数据库中的where语句
App\views.py

def get_person(request):persons = PersonModel.objects.filter()  # 默认没有条件, 得到所有数据persons = PersonModel.objects.filter(age__gt=100)  # age>100persons = PersonModel.objects.filter(age__gte=100)  # age>=100persons = PersonModel.objects.filter(age__lt=100)  # age<100persons = PersonModel.objects.filter(age__lte=100)  # age<=100persons = PersonModel.objects.filter(age=110)  # age=110print(persons.filter().filter())for p in persons:print('+++', p.name, p.age)return HttpResponse('查询成功')

就算只拿到一个数据,它也是一个查询集 QuerySet 。

查询集 QuerySet 可以继续去查询 filter ,内部会返回self ,所以它可以做链式调用。
在这里插入图片描述


count(): 返当前查询集中的对象个数

exists():判断查询集中是否有数据

App\views.py

def get_person(request):persons = PersonModel.objects.filter(age__gt=100)  # age>100for p in persons:print('+++', p.name, p.age)print(persons.exists())  # 查询集是否存在数据,如果存在则为True,否则为Falseprint(persons.count())  # 查询集中的数据个数return HttpResponse('查询成功')

values(): 获取指定列的值

可以传多个参数!返回包含字典的列表(保存了字段名和对应的值)

App\models.py

class PersonModel(models.Model):name = models.CharField(max_length=30, unique=True)age = models.IntegerField(default=18)# 表名class Meta:db_table = 'person'def __str__(self):return f'{self.name}-{self.age}'def __repr__(self):return f'{self.name}--{self.age}'

__str__方法大家应该都不陌生,它类似于Java当中的 toString 方法,可以根据我们的需要返回实例转化成字符串之后的结果。

__str__ 和 __repr__ 这两个函数都是将一个实例转成字符串。但是不同的是,两者的使用场景不同,其中__str__更加侧重展示。所以当我们print输出给用户或者使用str函数进行类型转化的时候,Python都会默认优先调用__str__函数。而__repr__更侧重于这个实例的报告,除了实例当中的内容之外,我们往往还会附上它的类相关的信息,因为这些内容是给开发者看的。所以当我们在交互式窗口输出的时候,它会优先调用__repr__。

App\views.py

def get_person(request):persons = PersonModel.objects.filter()  # 默认没有条件, 得到所有数据print("persons: ", persons)print("list(persons): ", list(persons))  # 将查询集强制转换成列表# values() :  列表套字典,包括字段和值print("persons.values(): ", persons.values())  # 列表套字典print("persons.values('name'): ", persons.values('name'))print("persons.values('name', 'age'): ", persons.values('name', 'age'))# values_list() : 列表套元组,只有值print("persons.values_list(): ", persons.values_list())print("persons.values_list('name', 'age'): ", persons.values_list('name', 'age'))  # 用法跟values一样return HttpResponse('查询成功')

结果:

persons:  <QuerySet [1--110,2--120,3--30,4--66,5--20, 甘雨--19]>
list(persons):  [1--110,2--120,3--30,4--66,5--20, 甘雨--19]
persons.values():  <QuerySet [{'id': 7, 'name': '景1元', 'age': 110}, {'id': 8, 'name': '景2元', 'age': 120}, {'id': 9, 'name': '景3元', 'age': 30}, {'id': 10, 'name': '景4元', 'age': 66}, {'id': 11, 'name': '景5元', 'age': 20}, {'id': 16, 'name': '甘雨', 'age': 19}]>
persons.values('name'):  <QuerySet [{'name': '景1元'}, {'name': '景2元'}, {'name': '景3元'}, {'name': '景4元'}, {'name': '景5元'}, {'name': '甘雨'}]>
persons.values('name', 'age'):  <QuerySet [{'name': '景1元', 'age': 110}, {'name': '景2元', 'age': 120}, {'name': '景3元', 'age': 30}, {'name': '景4元', 'age': 66}, {'name': '景5元', 'age': 20}, {'name': '甘雨', 'age': 19}]>
persons.values_list():  <QuerySet [(7, '景1元', 110), (8, '景2元', 120), (9, '景3元', 30), (10, '景4元', 66), (11, '景5元', 20), (16, '甘雨', 19)]>
persons.values_list('name', 'age'):  <QuerySet [('景1元', 110), ('景2元', 120), ('景3元', 30), ('景4元', 66), ('景5元', 20), ('甘雨', 19)]>

values_list():获取指定列的值

可以传多个参数!返回包含元组列表 (只保存值)


exclude() 排除,取反

App\views.py

def get_person(request):person = PersonModel.objects.filter(age__in=[20, 50, 30])  # inprint("person: ", person)person = PersonModel.objects.exclude(age__in=[20, 50, 30])  # not inprint("person: ", person)return HttpResponse('查询成功')

聚合

使用aggregate()函数返回聚合函数的值
    Avg:平均值
    Count:数量
    Max:最大
    Min:最小
    Sum:求和
from django.db.models import Count, Min, Max, Sum
PersonModel.objects.aggregate(Max(‘age’))
aggregate 是聚合的意思

App\views.py

from django.db.models import Count, Min, Max, Sum, Avg
def get_person(request):# 聚合函数result = PersonModel.objects.aggregate(Max('age'))  # 最大值print(result)  # {'age__max': 120}print(result['age__max'])  # 120return HttpResponse('查询成功')
def get_person(request):# 聚合函数result = PersonModel.objects.aggregate(Max('age'))  # 最大值 {'age__max': 120}result = PersonModel.objects.aggregate(Min('age'))  # 最小值 {'age__min': 19}result = PersonModel.objects.aggregate(Sum('age'))  # 求和 {'age__sum': 365}result = PersonModel.objects.aggregate(Avg('age'))  # 求平均 {'age__avg': 60.833333333333336}result = PersonModel.objects.aggregate(Count('age'))  # 统计个数 {'age__count': 6}print(result)return HttpResponse('查询成功')

排序

PersonModel.objects.all().order_by()
App\models.py

class PersonModel(models.Model):name = models.CharField(max_length=30, unique=True)age = models.IntegerField(default=18)# 表名class Meta:db_table = 'person'def __str__(self):return f'{self.id}-{self.name}-{self.age}'def __repr__(self):return f'{self.id}--{self.name}--{self.age}'

在这里插入图片描述
在这里插入图片描述

def get_person(request):# 聚合函数person = PersonModel.objects.all().order_by('age')  # 默认升序person = PersonModel.objects.all().order_by('age', 'id')  # 先age做升序,然后如果一样的用id来升序# 想要降序就加个负号-person = PersonModel.objects.all().order_by('age', '-id')  # 先age做升序,然后如果一样的用id来降序print(person)return HttpResponse('查询成功')

分页功能

手动分页

在这里插入图片描述

Day03DjangoPro01\urls.py

path('paginate/<int:page>/<int:per_page>/', paginate, name="paginate"),  # 分页功能

App\views.py

import math
# 分页功能
def paginate(request, page=1, per_page=10):# 页码:page=1# 每页数据数量:per_page=10'''思路:分页功能数据:[1,2,3,...,47]第几页   数据范围      下标范围        切片page=1, 1 ~ 10      0 ~ 9           [0:10]page=2, 11 ~ 20     10 ~ 19         [10:20]page=3, 21 ~ 30     20 ~ 29         [20:30]...page=n,                       [(n-1) * 10 : n * 10]page=page,                   [(page-1)*per_page : page*per_page]'''# 实现分页功能persons = PersonModel.objects.all()persons = persons[(page - 1) * per_page:page * per_page]# 求总页数total = PersonModel.objects.count()  # 总数据条数total_page = math.ceil(total / per_page)  # 总页数,可能是小数47/10,所以需要向上取整pages = range(1, total_page + 1)return render(request, "paginate.html", {"persons": persons,"pages": pages,"per_page": per_page})

templates\paginate.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>分页功能的实现</title><style>ul{list-style: none;padding: 0;}.btns{display: flex ;align-content: center;}.btns li{margin: 5px;}</style>
</head>
<body><h2>分页功能</h2><hr>页数:{{ per_page }}<ul class="btns">
{#        <li>#}
{#            <a href="{% url 'paginate' 1 10 %}"><button>1</button></a>#}
{#        </li>#}
{#         <li>#}
{#            <a href="{% url 'paginate' 2 10 %}"><button>2</button></a>#}
{#        </li>#}
{#         <li>#}
{#            <a href="{% url 'paginate' 3 10 %}"><button>3</button></a>#}
{#        </li>#}{% for page in pages %}<li><a href="{% url 'paginate' page per_page %}"><button>{{ page }}</button></a></li>{% endfor %}</ul><hr><ul>{% for person in persons %}<li>{{ person.name }} - {{ person.age }}</li>{% endfor %}</ul>
</body>
</html>

浏览器http://127.0.0.1:8000/paginate/1/10/

在这里插入图片描述

这就是最基本的分页功能啦!!!

Django中的自动分页器

Day03DjangoPro01\urls.py

path('paginate2/<int:page>/', paginate2, name="paginate2"),

App\views.py

# 分页器:自动分页
from django.core.paginator import Paginatordef paginate2(request, page=1):per_page = 10  # 每页数据数量all_data = PersonModel.objects.all()# 使用分页器paginator = Paginator(all_data, per_page)persons = paginator.page(page)  # 获取第page页的数据pages = paginator.page_range  # 页码的范围,可以遍历data = {"persons": persons,"pages": pages,"per_page": per_page}return render(request, "paginate2.html", data)

新建一个 templates\paginate2.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>分页器</title><style>ul{list-style: none;padding: 0;}.btns{display: flex ;align-content: center;}.btns li{margin: 5px;}</style>
</head>
<body><h2>分页器</h2><hr>页数:{{ per_page }}<ul class="btns">{% for page in pages %}<li><a href="{% url 'paginate2' page %}"><button>{{ page }}</button></a></li>{% endfor %}</ul><hr><ul>{% for person in persons %}<li>{{ person.name }} - {{ person.age }}</li>{% endfor %}</ul>
</body>
</html>

浏览器 http://127.0.0.1:8000/paginate2/1/
在这里插入图片描述

四、Django模型进阶

MYSQL和SQLite只是数据库不一样,配置不一样,其他的是一样的。

注意:我的 Django版本是4.2,MYSQL是8.0,Python 3.11.3

4.1 配置MySQL

1.安装mysql2.MySQL驱动使用mysqlclientpip install mysqlclient(如果上面的命令安装失败,则尝试使用国内豆瓣源安装:pip install -i https://pypi.douban.com/simple mysqlclient)(Linux Ubuntu下需要先安装:apt install libmysqld-dev再安装:apt install libmysqld-dev)3.在Django中配置和使用mysq1数据库
使用mysql数据库,settings中配置如下DATABASES={'default':{'ENGINE':'django.db.backends.mysql','NAME':'mydb','USER':'root','PASSWORD':'123456','HOST':'127.0.0.1','PORT':'3306',}}

实践

在这里插入图片描述

workon envdjango4
pip install mysqlclient

新建一个新的项目Day04DjangoPro01

在这里插入图片描述

DATABASES = {# MySQL 数据库配置'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'mydb',  # 数据库名字'USER': 'root',  # 数据库的用户名'PASSWORD': '123456',  # 用户名对应的密码'HOST': '127.0.0.1',  # 数据库的主机IP'PORT': '3306',  # 端口}# SQLite 数据库配置# 'default': {#     'ENGINE': 'django.db.backends.sqlite3',#     'NAME': BASE_DIR / 'db.sqlite3',# }
}

在这里插入图片描述

mysql -u root -p
输密码	我的是 123456
看一下数据库 show databases
创建新的数据库  create datadase 数据库名 charset=utf8;注意这个数据库要求是一个空的数据库
使用数据库use 数据库名show tables;

怎么安装MySQL这里就不写了, 我偷懒就直接用小P(phpstudy)吧。网盘链接:https://pan.baidu.com/s/1Hc_oHRem4pY3VSmqCrbCjw?pwd=6666
提取码:6666

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们知道Django项目里面它自带本身有很多迁移文件,我们是不是可以把内部的迁移文件生成到数据库里面去呀?
生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

在这里插入图片描述
在这里插入图片描述
然后就成功啦!!!

报错1:django.db.utils.NotSupportedError: MySQL 8 or later is required (found 5.7.26).

说是MYSQL兼容问题
解决办法:数据库更新到 8.0就好啦

报错2:RuntimeWarning: Got an error checking a consistent migration history performed for database connection ‘default’: (1045, “Access denied for user ‘root’@‘localhost’ (using password: YES)”)

用户密码错了,所以连接不上。

4.2 多模块关联关系

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多个模块关联关联分类- ForeignKey:一对多,将字段定义在多的端中- ManyToManyField: 多对多,将字段定义在两端的任意一端中- OneToOneField:一对一,将字段定义在任意一端中一对多关系,举例说明 (一对一, 多对多类似) :一个班级可以有多个学生,一个学生只能属于一个班级class Grade(models.Model):name = models.CharField(max_length=20)class Student(models.Model):name = models.CharField(max_length=20)grade = models.ForeignKey(Grade)对象的使用:正向 (在Student这边,有grade属性的这一边):获取学生所在班级(对象):stu.grade获取学生所在班级的属性:stu.grade.name反向(在Grade这边):获取班级的所有学生(获取Manager对象):grade.student_set获取班级的所有学生(获取QuerySet查询集): grade.student_set.all()filter(),get()等操作中的使用:正向(在Student这边,有grade属性的这一边)Student.objects.filter(属性__name='1'): Student.objects.filter(grade__name='1')反向(在Grade这边)Grade.objects.filter(类名小写__id=7): Grade.objects.filter(student__id=7)

xx_set 其实是一个管理器 ,类似于 objects ,通过它 .filter() 或者 .all() 就可以拿到所有查询到的结果(叫查询集)。

4.3 Model连表结构

一对多: models.ForeignKey(其他表)
多对多: models.ManyToManyField(其他表)
一对一: models.OneToOneField(其他表)
应用场景:一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)例如: 创建用户信息时候,需要选择一个用户类型[普通用户][金牌用户][铂金用户]多对多:在某表中创建一行数据时,有一个可以多选的下拉框。 (猫眼App,淘票票,格拉瓦电影)例如:创建用户信息,需要为用户指定多个爱好。一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了)例如:有个身份证表,有个person表。每个人只能有一张身份证,一张身份证也只能对应一个人,这就是一对一关系。

4.3.1 一对多关联

一对多关系,即外键为什么要用一对多。先来看一个例子。有一个用户信息表,其中有个用户类型字段,存储用户的用户类型。如下:class UserInfo(models.Model):username = models.CharField(max_length=32)age = models.IntegerField()user_type = models.CharField(max_length=10)不使用外键时用户类型存储在每一行数据中。如使用外键则只需要存储关联表的id即可,能够节省大量的存储空间。同时使用外键有利于维持数据完整性和一致性.当然也有缺点,数据库设计变的更复杂了。每次做DELETE 或者UPDATE都必须考虑外键约束。刚才的例子使用外键的情况: 单独定义一个用户类型表:class UserType(models.Model):caption = models.CharField(max_length=32)class UserInfo(models.Model):user_type = models.ForeignKey('UserType')username = models.CharField(max_length=32)age = models.IntegerField()我们约定:正向操作: ForeignKey在UserInfo表里,如果根据UserInfo去操作就是正向操作。反向操作: ForeignKey不在UserType里,如果根据UserType去操作就是反向操作。一对多的关系的增删改查:正向操作:1)创建对象实例,然后调用save方法obj = UserInfo(name='li',age=44, user_type_id=2)obj.save()2)使用create方法UserInfo.objects.create(name='li',age=44,user_type_id=2)3)使用get_or_create方法,可以防止重复UserInfo.objects.get_or_create(name='li',age=55,user_type_id=2)4)使用字典dic = {'name':'zhangsan', 'age':18, 'user_type_id':3}UserInfo.objects.create(**dic)5)通过对象添加usertype = UserType.objects.get(id=1)UserInfo.objects.create(name='li',age=55,user_type=usertype)删和普通模式一样删除即可。如:UserInfo,objects.filter(id=1).delete()改和普通模式一样修改即可。如:UserInfo.objects.filter(id=2).update(user_type_id=4)查正向查找所有用户类型为钻石用户的用户,使用双下划线:users = UserInfo.objects,filter(user_type__caption__contains='钻石')# __contains是过滤条件 like '%钻石%'正向获取关联表中的属性可以直接使用点.语法,比如:获取users查询集中第一个用户的caption:users[0].user_type.caption反向操作:(一般使用正向增即可)通过usertype来创建userinfo1)通过userinfo_set的create方法#获取usertype实例ut = UserType.objects.get(id=2)#创建userinfout.userinfo_set.create(name='smith',age=33)删删除操作可以在定义外键关系的时候,通过on_delete参数来配置删除时做的操作。on_delete参数主要有以下几个可选值:models.CASCADE	默认值(Django1.11),表示级联删除,即删除UserType时,相关联的UserInfo也会被删除。models.PROTECT	保护模式, 阻止级联删除。如果UserType还有关联的User存在,那么他就阻止你不让你删除。删除的时候可能报错。models.SET_NULL 置空模式,设为null,null=True参数必须具备。删除UserType的时候不会将所关联的用户删掉,而是把关联的User外键的值置空models.SET_DEFAULT	置默认值	设为默认值,default参数必须具备。如果删除UserType,关联的User外键的值不置空,设置成指定的值models.SET(函数名)	删除的时候重新动态指向一个实体访问对应元素,可传函数models.DO_NOTHING 什么也不做。 (Django1.11)。外键不会动,但是这样关联的外键就是错误的。注意:修改on_delete参数之后需要重新同步数据库,如果使用的话。需要重新迁移!!!一般建议 PROTECT 或者 SET_NULL 或者 SET_DEFAULT改和普通模式一样,不会影响级联表查通过usertype对象来查用户类型为1的用户有哪些obj=UserType.objects.get(id=1)obj.userinfo_set.all()可以通过在定义foreignkey时指定related_name来修改默认的userinfo_set,比如指定related_name为infouser_type = models.ForeignKey('UserType', related_name='info')指定related_name之后,反向查的时候就变成了obj.info.all()获取用户类型为1且用户名为shuaige的用户obj.info.filter(username='shuaige')外键关系中,django自动给usertype加了一个叫做userinfo的属性。使用双下划线。可以通过userinfo提供的信息来查usertype (了解)user_type_obj = UserType.objects.get(userinfo__username='zs')

生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

实践

OneToMany\models.py

from django.db import models# 一对多 = 1:N
# 用户类型:用户 = 1:N
# 一种用户类型: 可以有多个用户
# 一个用户:只属于一个用户类型# 用户类型
class UserType(models.Model):name = models.CharField(max_length=30)# 用户
class User(models.Model):name = models.CharField(max_length=30)age = models.IntegerField(default=18)# 外键user_type = models.ForeignKey(UserType, on_delete=models.CASCADE)# models.CASCADE 级联删除,即删除UserType时,相关联的User也会被删除。

生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

在这里插入图片描述

注意:用户表(User)中我们代码写的是user_type,但是数据库写的是user_type_id,这个有区别,它的类型跟 用户类型(UserType) 的主键是一样的。我们在后面使用的时候,可以使用user_type也可以使用user_type_id,但其实得到user_type呢相当于得到上面UserType对象了 ,得到这个对象之后再去 .id .name是可以的。

在这里插入图片描述

增加数据

OneToMany\views.py

from django.shortcuts import render, HttpResponse
from OneToMany.models import *# 一对多关系# 添加数据
def add_user(request):# 给UserType添加数据user_types = ['青铜', '白银', '黄金', '钻石']for name in user_types:UserType.objects.create(name=name)return HttpResponse("添加成功")

根路由Day04DjangoPro01\urls.py

from django.contrib import admin
from django.urls import path
from OneToMany import views as onetomany_viewurlpatterns = [path('admin/', admin.site.urls),# onetomanypath('onetomany/adduser/', onetomany_view.add_user ),
]

运行python .\manage.py runserver

http://127.0.0.1:8000/onetomany/adduser/

在这里插入图片描述
添加完之后,代码注释了,现在写添加User表,OneToMany\views.py
第一种用 user_type_id= id

def add_user(request):# # 用完就注释# # 给UserType添加数据# user_types = ['青铜', '白银', '黄金', '钻石']# for name in user_types:#     UserType.objects.create(name=name)# 给User表添加数据for i in range(11, 30):User.objects.create(name=f'清风-{i}',age=i,user_type_id=i % 4 + 1)  # i % 4 + 1 = 1~4return HttpResponse("添加成功")

http://127.0.0.1:8000/onetomany/adduser/

在这里插入图片描述
第二种用 user_type= 用户类型的对象

def add_user(request):# # 用完就注释# # 给UserType添加数据# user_types = ['青铜', '白银', '黄金', '钻石']# for name in user_types:#     UserType.objects.create(name=name)# 给User表添加数据for i in range(11, 30):# User.objects.create(name=f'清风-{i}', age=i,#                     user_type_id=i % 4 + 1)  # i % 4 + 1 = 1~4User.objects.create(name=f'微泫-{i}', age=i + 100,user_type=UserType.objects.get(pk=i % 4 + 1))return HttpResponse("添加成功")

http://127.0.0.1:8000/onetomany/adduser/
在这里插入图片描述

删除数据

根路由Day04DjangoPro01\urls.py

path('onetomany/deluser/', onetomany_view.del_user),

OneToMany\views.py
删除用户数据

# 删除数据
def del_user(request):# 删除User数据User.objects.filter(id=6).delete()return HttpResponse('删除成功')

http://127.0.0.1:8000/onetomany/deluser/

删除UserType数据

def del_user(request):# 删除User数据# User.objects.filter(id=6).delete()# 删除UserType数据UserType.objects.filter(id=2).delete()return HttpResponse('删除成功')

http://127.0.0.1:8000/onetomany/deluser/

执行之后,可以观察到UserType表删除了id=2的数据,User表删除了外键user_type_id是2的数据。

因为前面Models设置了 models.CASCADE 表示级联删除,即删除UserType时,相关联的User也会被删除。

试试其他的模式

OneToMany\models.py

user_type = models.ForeignKey(UserType, on_delete=models.PROTECT)  # 保护模式

浏览器 http://127.0.0.1:8000/onetomany/deluser/
在这里插入图片描述

user_type = models.ForeignKey(UserType, on_delete=models.SET_NULL, null=True)  # 置空模式
user_type = models.ForeignKey(UserType, on_delete=models.SET_DEFAULT, default=1)  # 置默模式
def fn():return 4# 用户
class User(models.Model):name = models.CharField(max_length=30)age = models.IntegerField(default=18)# 外键# user_type = models.ForeignKey(UserType, on_delete=models.CASCADE)# models.CASCADE 级联删除,即删除UserType时,相关联的User也会被删除。# user_type = models.ForeignKey(UserType, on_delete=models.PROTECT)  # 保护模式# user_type = models.ForeignKey(UserType, on_delete=models.SET_NULL, null=True)  # 置空模式# user_type = models.ForeignKey(UserType, on_delete=models.SET_DEFAULT, default=1)  # 置默模式user_type = models.ForeignKey(UserType, on_delete=models.SET(fn))  # 删除的时候重新动态指向一个实体访问对应元素,可传函数

其实models.SET(函数名) 跟 models.SET_DEFAULT 差不多

user_type = models.ForeignKey(UserType, on_delete=models.DO_NOTHING)  # 什么都不做

测试完我们还是用 user_type = models.ForeignKey(UserType, on_delete=models.PROTECT) # 保护模式

生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

修改数据

和普通模式一样,不会影响级联表

根路由Day04DjangoPro01\urls.py

path('onetomany/updateuser/', onetomany_view.update_user),

OneToMany\views.py

# 修改数据
def update_user(request):# 修改UserTypeUserType.objects.filter(id=1).update(name="王者")return HttpResponse('修改成功')
def update_user(request):# 修改UserType# UserType.objects.filter(id=1).update(name="王者")# 修改UserUser.objects.filter(id=2).update(age=1000)return HttpResponse('修改成功')

http://127.0.0.1:8000/onetomany/updateuser/

查询数据

根路由Day04DjangoPro01\urls.py

path('onetomany/getuser/', onetomany_view.get_user),

正向查询:有属性的地方直接查询,写了外键的地方直接查就是正向查询,通过 用户User 查找他所对应的 用户类型UserType

反向查询:通过 用户类型UserType 查找 用户User


OneToMany\views.py
前三种查询

1、正向查询
2、反向查询
    UserType对象.user_set.all()
    主表对象.从表名_set.all()
3、在filter中
     User.objects.filter(UserType)
    相比于第2种_set , 从时间成本来考虑的话还是使用 filter 筛选效率更高一些。

# 查询数据
def get_user(request):# 1.正向查询:通过用户查找用户类型user = User.objects.get(id=2)print("用户:", user.name, user.age, user.user_type, user.user_type_id)# user.user_type => UserType对象print("用户所属的类型:", user.user_type.id, user.user_type.name)  # User所属UserType的所有数据print('-' * 60)# 2.反向查询:通过用户类型查找用户utype = UserType.objects.get(pk=1)print('UserType自己的属性', utype.id, utype.name)# user_set: 是内部自动生成的属性, 可以让你反向查询到所有用户User集合print(utype.user_set)  # OneToMany.User.None 你可以认为它是一个管理对象print(type(utype.user_set))  # RelatedManager 关联的管理器对象# <class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>print(utype.user_set.all())  # 所有数据的 查询集 QuerySetprint('+' * 60)# 3.在filter中还可以这么用# 比如:查找用户类型名称为'黄金'的所有用户users = User.objects.filter(user_type=UserType.objects.get(name='黄金'))  # 传入UserType对象users = User.objects.filter(user_type_id=4)  # 传入user_type_idusers = User.objects.filter(user_type__name='黄金')  # 传入UserType对象的name属性作为条件# filter(user_type__name='黄金') 加两个下划线__反向引用去查找print(users)return HttpResponse("查询成功")

第四种查询

4、related_name: 关联名称

related_name 是我们用于反向查找的内容。通常,为所有外键提供 related_name 是一个好习惯,而不是使用 Django 的默认相关名称。
related_name 默认是 小写外键的表名_set 或者也可以这么理解 子表的表名小写_set
主表 UserType表:表中有一个主键被其他表用来当外键的表。
从表(子表)User表:把另外一个表中的主键当做自己的外键的表。

    主表对象.从表名_set.all()
    主表对象.设置的related_name.all()

OneToMany\models.py

在这里插入图片描述

class User(models.Model):name = models.CharField(max_length=30)age = models.IntegerField(default=18)# 外键# related_name: 关联名称,设置反向查找的名称,原本 反向查找 使用的是user_set改为usersuser_type = models.ForeignKey(UserType, on_delete=models.PROTECT,related_name='users'  # 建议使用)

生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

OneToMany\views.py

def get_user(request):print('=' * 60)# 4.related_name: 关联名称utype = UserType.objects.get(pk=1)# print(utype.user_set.all())  # 会报错 AttributeError 'UserType' object has no attribute 'user_set'# 使用了related_name就不可以在使用带_set的属性print(utype.users.all())  # <QuerySet [<User: User object (2)>, ...] >return HttpResponse("查询成功")

执行完迁移之后,现在我们不能再用user_set了,使用就会报错AttributeError ‘UserType’ object has no attribute ‘user_set’。如果我们使用了related_name,原来的属性user_set就不能使用了。

使用了related_name就不可以在使用带_set的属性

在这里插入图片描述

4.3.2 多对多关联

在这里插入图片描述
多对多其实就是两个一对多,如果不在乎中间表 ,我们可以直接建两边的表,中间的不用管。

多对多关系针对多对多关系django会自动创建第三张表。也可以通过through参数指定第三张表。用户和组是典型的多对多关系:class Group(models.Model):name = models.CharField(max_length=20)def __str__(self):return self.nameclass User(models.Model):name = models.CharField(max_length=64)password = models.CharField(max_length=64)groups = models.ManyToManyField(Group)def __str__(self):return self.name操作::先分别创建user和group,再使用add关联u = User(name='aa', password='123')u.save()g = Group(name='g5')g.save()通过Manager对象使用add()方法u.groups.add(g) 或 g.user_set.add(u):和一对多类似,删除user或group会级联删除user_groups表中的关联数据改:和一对多类似,只修改当前表查:正向:查询id=2的用户所在的所有组groupu = User.objects.get(id=2)u.groups.al1()反向:查询id=1的组中包含的所有用户g = Group.objects.get(id=1)g.user_set.all()

实践

新建一个应用 叫 ManyToMany
python manage.py startapp ManyToMany
创建好之后一定要记得注册 Day04DjangoPro01\settings.py
在这里插入图片描述
ManyToMany\models.py

from django.db import models# 多对多
# 用户:电影 = N:M
# 一个用户可以收藏多部电影,一部电影可以被不同用户收藏# 电影
class Movie(models.Model):name = models.CharField(max_length=52)duration = models.IntegerField(default=90)  # 电影时长:默认90分钟# 用户
class User(models.Model):name = models.CharField(max_length=30)age = models.IntegerField(default=18)# 多对多关系movies = models.ManyToManyField(Movie)

生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate

在这里插入图片描述
可以看到创建了三个表。

添加数据

子路由不写了,直接写根路由上吧,Day04DjangoPro01\urls.py

在这里插入图片描述

写视图函数ManyToMany\views.py

from django.shortcuts import render, HttpResponse
from ManyToMany.models import *# 多对多# 增加数据
def add(request):# 添加User数据for i in range(1, 10):User.objects.create(name=f'张三{i}', age=i)# 添加Moviefor i in range(1, 10):Movie.objects.create(name=f'消失的她{i}', duration=100+i)return HttpResponse("添加成功")

http://127.0.0.1:8000/manytomany/add/

在这里插入图片描述
添加完这两个表之后,我们要添加中间表的数据

写法一

ManyToMany\views.py

def add(request):# 让 张三1 收藏 消失的她1user = User.objects.get(name='张三1')movie = Movie.objects.get(name='消失的她1')# 添加收藏user.movies.add(movie)  # 用户收藏电影return HttpResponse("添加成功")

浏览器 http://127.0.0.1:8000/manytomany/add/
可以多刷新几次试试,添加是不会重复的
在这里插入图片描述

写法二

ManyToMany\views.py

def add(request):# 让 张三1 收藏 消失的她2user = User.objects.get(name='张三1')movie = Movie.objects.get(name='消失的她2')# 添加收藏# user.movies.add(movie)  # 用户收藏电影movie.user_set.add(user)  # 让用户收藏电影return HttpResponse("添加成功")

浏览器 http://127.0.0.1:8000/manytomany/add/
在这里插入图片描述


手动添加点数据吧
在这里插入图片描述


修改数据

一般多对多关系中不涉及到中间表修改,如果要对单表修改那就跟前面的一样了。

删除数据

Day04DjangoPro01\urls.py

path('manytomany/delete/', manytomany_view.delete),
删除电影

ManyToMany\views.py

def delete(request):# 删除User# User.objects.get(id=9).delete()# 删除Movie# Movie.objects.get(id=9).delete()# 删除中间表user = User.objects.get(name='张三1')user.movies.filter(name='消失的她2').delete()  # 删除用户收藏的电影#  user.movies.filter 这里已经到了Movie,所以可以使用name,这个name是Movie的namereturn HttpResponse('删除成功')

http://127.0.0.1:8000/manytomany/delete/

效果:电影表中 name=消失的她2 都被删了,中间表有关 电影消失的她2 的数据 也被删了。

注意: 假如 中间表 张三1 没有收藏 消失的她2 的记录,中间表、电影表那么就不会有任何效果。

删除用户

效果其实类似,反过来就好了

ManyToMany\views.py

def delete(request):movie = Movie.objects.get(name='消失的她1')movie.user_set.filter(name='张三1').delete()return HttpResponse('删除成功')

http://127.0.0.1:8000/manytomany/delete/

查询

Day04DjangoPro01\urls.py

path('manytomany/get/', manytomany_view.get_user_movie),

ManyToMany\views.py

# 查询数据
def get_user_movie(request):# 获取用户收藏的所有电影user = User.objects.get(name='张三1')print(user.movies.all())# 获取电影被哪些用户收藏了movie = Movie.objects.get(name='消失的她1')print(movie.user_set.all())return HttpResponse("查询成功")

http://127.0.0.1:8000/manytomany/get/在这里插入图片描述
注意:我的数据表里的数据我另外又加回来了
    电影:1 消失的她1
    用户:1 张三1
在这里插入图片描述

4.3.3 一对一

一对一关系一对一不是数据库的一个连表操作,而是Django独有的一个连表操作。一对一关系相当于是特殊的一对多关系,只是相当于加了unique=True。一个人只能有一张身份证,一张身份证对应一个人,是一个典型的一对一关系。class IdCard(models.Model):idnum = models.IntegerField()def __str__(self):return str(self.idnum)class Person(models.Model):idcard = models.OneToOneField(IdCard) # 外键name = models.CharField(max_length=20)def __str__(self):return self.name一对一关系比较简单。两种表互相都有对方。比如:>>> lisi = Person.objects.get(id=3)>>> lisi.idcard<IdCard: 123456>>>> ids = IdCard.objects.get(id=3)>>> ids.person<Person: lisi>

实践

这里演示查询就可以了,其他的跟一对多差不多。

新建一个应用 叫 OneToOne
python manage.py startapp OneToOne
创建好之后一定要记得注册 Day04DjangoPro01\settings.py

在这里插入图片描述

OneToOne\models.py

from django.db import models# 身份证
class IDCard(models.Model):idcard_num = models.CharField(max_length=18, unique=True)address = models.CharField(max_length=200)# 用户表
class Person(models.Model):name = models.CharField(max_length=20)age = models.IntegerField(default=18)sex = models.BooleanField(default=True)  # True是男# 一对一关系idcard = models.OneToOneField(IDCard, on_delete=models.PROTECT)  # 外键

生成迁移文件: python manage.py makemigrations
执行迁移: python manage.py migrate
在这里插入图片描述

一对一 和 一对多 一样 ,只有两个表,不会产生中间表。

在这里插入图片描述

注意: Unique,唯一约束

数据库插入点数据
在这里插入图片描述

增删改

和一对多是类似的。

查询

和一对多是类似的,不同的是一对一得到的是 对方的对象 而不是 一个查询集。
Day04DjangoPro01\urls.py
在这里插入图片描述

OneToOne\views.py

from django.shortcuts import render, HttpResponse
from OneToOne.models import *# 一对一# 查询
def get(request):#  查找某个用户对应的身份证信息person = Person.objects.get(pk=1)print(person.idcard)  # 对象 IDCard object (1)print(person.idcard.idcard_num, person.idcard.address)# 查找身份证对应的用户idcard = IDCard.objects.get(pk=2)print(idcard.person)  # 对象 Person object (2)# 注意不是idcard.person_setprint(idcard.person.name, idcard.person.age, idcard.person.sex)return HttpResponse("查询成功")

注意:不是idcard.person_set ,是 idcard.person

http://127.0.0.1:8000/onetoone/get/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/43834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据课程K2——Spark的RDD弹性分布式数据集

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的RDD结构; ⚪ 掌握Spark的RDD操作方法; ⚪ 掌握Spark的RDD常用变换方法、常用执行方法; 一、Spark最核心的数据结构——RDD弹性分布式数据集 1. 概述 初学Spark时,把RDD看…

优于立方复杂度的 Rust 中矩阵乘法

优于立方复杂度的 Rust 中矩阵乘法 迈克克维特 跟随 发表于 更好的编程 6 分钟阅读 7月 <> 143 中途&#xff1a;三次矩阵乘法 一、说明 几年前&#xff0c;我在 C 年编写了 Strassen 矩阵乘法算法的实现&#xff0c;最近在 Rust 中重新实现了它&#xff0c;因为我继续…

基于Bsdiff差分算法的汽车OTA升级技术研究(学习)

摘要 针对汽车OTA整包升级时&#xff0c;用户下载时间长&#xff0c;升级时间长&#xff0c;设备服务器端压力大等问题&#xff0c;本文提出了一种基于Bsdiff差分算法的汽车OTA升级技术。该算法能够对比新旧版本的差异&#xff0c;进行差分文件下载&#xff0c;减少软件包的下…

大数据面试题:Spark的任务执行流程

面试题来源&#xff1a; 《大数据面试题 V4.0》 大数据面试题V3.0&#xff0c;523道题&#xff0c;679页&#xff0c;46w字 可回答&#xff1a;1&#xff09;Spark的工作流程&#xff1f;2&#xff09;Spark的调度流程&#xff1b;3&#xff09;Spark的任务调度原理&#xf…

Python Opencv实践 - 图像仿射变换

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) rows,cols img.shape[:2] print(img.shape[:2])#使用getAffineTransform来获得仿射变换的矩阵M #cv.getAffineTransform(…

设计模式之适配器模式(Adapter)的C++实现

1、适配器模式的提出 在软件功能开发中&#xff0c;由于使用环境的改变&#xff0c;之前一些类的旧接口放在新环境的功能模块中不再适用。如何使旧接口能适用于新的环境&#xff1f;适配器可以解决此类问题。适配器模式&#xff1a;通过增加一个适配器类&#xff0c;在适配器接…

汽车领域专业术语

1. DMS/OMS/RMS/IMS DMS&#xff1a;即Driver Monitoring System&#xff0c;监测对象为Driver&#xff08;驾驶员&#xff09;。DMS三大核心&#xff1a; OMS&#xff1a;即Occupancy Monitoring System&#xff0c;监测对象为乘客。 RMS&#xff1a;后排盲区检测系统 IMS&…

提升大数据技能,不再颓废!这6家学习网站是你的利器!

随着国家数字化转型&#xff0c;大数据领域对人才的需求越来越多。大数据主要研究计算机科学和大数据处理技术等相关的知识和技能&#xff0c;从大数据应用的三个主要层面&#xff08;即数据管理、系统开发、海量数据分析与挖掘&#xff09;出发&#xff0c;对实际问题进行分析…

Linux Vm上部署Docker

创建ubutu虚拟机并远程连接&#xff0c; 参考 https://blog.csdn.net/m0_48468018/article/details/132267096 在终端中切换到root用户&#xff0c;并安装docker服务 2.1 切换到root用户 sudo su2.2 安装docker服务 , 参考 https://docs.docker.com/engine/install/ubuntu/ …

百望云联合华为发布票财税链一体化数智解决方案 赋能企业数字化升级

随着数据跃升为数字经济关键生产要素&#xff0c;数据安全成为整个数字化建设的重中之重。为更好地帮助企业发展&#xff0c;中央及全国和地方政府相继出台了多部与数据相关的政策法规&#xff0c;鼓励各领域服务商提供具有自主创新的软件产品与服务&#xff0c;帮助企业在合规…

聊聊在集群环境中本地缓存如何进行同步

前言 之前有发过一篇文章聊聊如何利用redis实现多级缓存同步。有个读者就给我留言说&#xff0c;因为他项目的redis版本不是6.0版本&#xff0c;因此他使用我文章介绍通过MQ来实现本地缓存同步&#xff0c;他的同步流程大概如下图 他原来的业务流程是每天凌晨开启定时器去爬取…

Redis数据结构——快速列表quicklist、快表

定义 Redis中的数据结构&#xff0c;链表和压缩列表这两种数据结构是列表对象的底层实现方式。 当时考虑到链表的附加空间太大&#xff0c;节点的内存都是单独分配的&#xff0c;还会导致内存碎片化问题严重。 因此从Redis3.2开始&#xff0c;对列表的底层数据结构进行了改造&…

CMake语法复习

前言 此文总结了库的制作和一些CMake常用的一些语法。 一&#xff1a;创建静态库和动态库 静态库的生成和使用 动态库的生成和使用 二&#xff1a;使用CMake来生成Makefile&#xff0c;生成可执行文件 顶层目录下的CMakeLists.txt project(HELLO) add_subdirectory(libhell…

Python项目实战:基于napari的3D可视化(点云+slice)

文章目录 一、napari 简介二、napari 安装与更新三、napari【巨巨巨大的一个BUG】四、napari 使用指南4.1、菜单栏&#xff08;File View Plugins Window Help&#xff09;4.2、Window&#xff1a;layer list&#xff08;参数详解&#xff09;4.3、Window&#xff1a;layer…

FPGA:uart原理+tx发送模块+rx接收模块

文章目录 一、串口通信二、UART通信三、tx发送模块四、rx模块接收 一、串口通信 处理器与外部设备通信的两种方式&#xff1a; 串行通信&#xff1a; 指数据的各个位使用多条数据线同时进行传输。 并行通信&#xff1a; 将数据分成一位一位的形式在一条数据线上逐个传输。 串…

Pycharm找不到Conda可执行文件路径(Pycharm无法导入Anaconda已有环境)

在使用Pycharm时发现无法导入Anaconda创建好的环境&#xff0c;会出现找不到Conda可执行文件路径的问题。 解决 在输入框内输入D:\anaconda3\Scripts\conda.exe&#xff0c;点击加载环境。 注意前面目录是自己Anaconda的安装位置&#xff0c;之后就可以找到Anaconda的现有环…

Mr. Cappuccino的第63杯咖啡——Spring之AnnotationConfigApplicationContext源码分析

Spring之AnnotationConfigApplicationContext源码分析 源码分析 源码分析 以上一篇文章《Spring之Bean的生命周期》的代码进行源码分析 AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(SpringConfig02.class); LifeCycleBe…

第17集丨Vue中的render函数

目录 一、脚手架中不能使用template配置二、基本使用三、关于不同版本的Vue 一、脚手架中不能使用template配置 // 引入vue import Vue from vue import App from ./Appnew Vue({el:#app,template:<h1>hhh</h1>,comments:{App},})上面案例中&#xff0c;配置了temp…

Mac 使用 rar 命令行工具解压和压缩文件

在 Mac 中常遇到的压缩文件有 zip 和 rar 格式的&#xff0c;如果是 zip 格式的 Mac 系统默认双击一下文件就能直接解压了&#xff0c;但 rar 文件就不行。 需要额外下载 rar 工具了实现。 第一步&#xff1a;下载 rar 工具 工具网址&#xff1a;https://www.rarlab.com/dow…

大语言模型之四-LlaMA-2从模型到应用

最近开源大语言模型LlaMA-2火出圈&#xff0c;从huggingface的Open LLM Leaderboard开源大语言模型排行榜可以看到LlaMA-2还是非常有潜力的开源商用大语言模型之一&#xff0c;相比InstructGPT&#xff0c;LlaMA-2在数据质量、培训技术、能力评估、安全评估和责任发布方面进行了…