Django模型基础

news2024/11/15 15:28:16

文章目录

  • 一、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 models


class 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 admin

from 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, HttpResponse

from App.models import *


# 增加数据
def add_person(request):
    # 方式一
    try:
        p = PersonModel()
        p.name = '清风'
        p.age = 30
        p.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 > 1
	
	Author.objects.filter(id__gte=1)# 获取id大于或等于1的值
	# select * from Author where id >= 1
	
	Author.objects.filter(id__lt=10)# 获取id小于10的值
	# select * from Author where id < 10
	
	Author.objects.filter(id__lte=10)# 获取id小于或等于10的值
	# select * from Author where id <= 10
	
	Author.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
	# select * from Author where id < 10 and id > 1
		
	Author.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, Sum
	Author.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>100
    persons = PersonModel.objects.filter(age__gte=100)  # age>=100
    persons = PersonModel.objects.filter(age__lt=100)  # age<100
    persons = PersonModel.objects.filter(age__lte=100)  # age<=100
    persons = PersonModel.objects.filter(age=110)  # age=110
    
    print(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>100
    for p in persons:
        print('+++', p.name, p.age)

    print(persons.exists())  # 查询集是否存在数据,如果存在则为True,否则为False
    print(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])  # in
    print("person: ", person)
    person = PersonModel.objects.exclude(age__in=[20, 50, 30])  # not in
    print("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'])  # 120
    return 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 Paginator

def 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.安装mysql

2.MySQL驱动
	使用mysqlclient
		pip 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来创建userinfo
				1)通过userinfo_set的create方法
					#获取usertype实例
					ut = UserType.objects.get(id=2)
					#创建userinfo
					ut.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为info
					user_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_view

urlpatterns = [
    path('admin/', admin.site.urls),

    # onetomany
    path('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~4

    return 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~4

        User.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):
    # 修改UserType
    UserType.objects.filter(id=1).update(name="王者")

    return HttpResponse('修改成功')
def update_user(request):
    # 修改UserType
    # UserType.objects.filter(id=1).update(name="王者")

    # 修改User
    User.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())  # 所有数据的 查询集 QuerySet

    print('+' * 60)
    # 3.在filter中还可以这么用
    # 比如:查找用户类型名称为'黄金'的所有用户
    users = User.objects.filter(user_type=UserType.objects.get(name='黄金'))  # 传入UserType对象
    users = User.objects.filter(user_type_id=4)  # 传入user_type_id
    users = 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改为users
    user_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.name
		
		class 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的用户所在的所有组group
				u = 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)

    # 添加Movie
    for 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 收藏 消失的她1
    user = 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 收藏 消失的她2
    user = 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的name

    return 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_set
    print(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.coloradmin.cn/o/900434.html

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

相关文章

C#生产流程控制(串行,并行混合执行)

开源框架CsGo https://gitee.com/hamasm/CsGo?_fromgitee_search 文档资料&#xff1a; https://blog.csdn.net/aa2528877987/article/details/132139337 实现效果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37…

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

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

图像处理常见的两种拉流方式

传统算法或者深度学习在进行图像处理之前&#xff0c;总是会首先进行图像的采集&#xff0c;也就是所谓的拉流。解决拉流的方式有两种&#xff0c;一个是直接使用opencv进行取流&#xff0c;另一个是使用ffmpeg进行取流&#xff0c;如下分别介绍这两种方式进行拉流处理。 1、o…

基于深度学习创建-表情符号--附源码

表情符号深度学习概述 如今,我们使用多种表情符号或头像来表达我们的心情或感受。它们充当人类的非语言线索。它们成为情感识别、在线聊天、品牌情感、产品评论等的关键部分。针对表情符号驱动的故事讲述的数据科学研究不断增加。 从图像中检测人类情绪非常流行,这可能是由…

【ROS】参数服务器--理论模型与参数操作(C++)

一、概念介绍 参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据。 作用&#xff1a;存储一些多节点…

Gradio详细文档——快速部署可视化人工智能应用

前言 Gradio是一个开源的Python库&#xff0c;用于快速构建机器学习和数据科学演示的应用。它可以帮助你快速创建一个简单漂亮的用户界面&#xff0c;以便向客户、合作者、用户或学生展示你的机器学习模型。此外&#xff0c;还可以通过自动共享链接快速部署模型&#xff0c;并获…

消息中间件相关面试题

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱发博客的嗯哼&#xff0c;爱好Java的小菜鸟 &#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&#x1f44d;一下博主哦 &#x1f4dd;社区论坛&#xff1a;希望大家能加入社区共同进步…

VALN-hybrid模式

实验拓扑及要求 一、实验思路 1.R1-R3按要求配置&#xff0c;R2不划分vlan使其全部都可以访问 2.交换机和路由器的交换机直连接口设为hybrid模式且R4-R6不带vlan标签访问路由器 3.交换机和交换机的两个直连接口设为hybrid模式且只允许R4-R6所在vlan标签通过 4.R4-R6只允许其…

野火i.mx 6ull上手

目录 屏幕驱动打印信息 实现触摸屏校验 开发板连接WIFI 连接操作 申请路由器动态IP和ping网络通断 WiFi信息保存位置 常用wifi操作&#xff08;wpa_cli工具&#xff09; NFS网络文件系统共享 虚拟机安装NFS服务器 开发板安装NFS客户端 控制开发板 找出硬件设备所对…

Debian10: 安装nut服务器(UPS)

UPS说明&#xff1a; UPS的作用就不必讲了&#xff0c;我选择是SANTAKTGBOX-850&#xff0c;规格为 850VA/510W&#xff0c;可以满足所需&#xff0c;关键是Debian10自带了驱动可以支持&#xff0c;免去安装驱动&#xff0c;将UPS通过USB线连接服务器即可&#xff0c;如下图所示…

wvp-gb28181-pro较新版本的调整说明

gitee地址 wvp-GB28181-pro: WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台&#xff0c;负责实现核心信令与设备管理后台部分&#xff0c;支持NAT穿透&#xff0c;支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联&#xff0c;支持rtsp/rtmp等…

Git判断本地是否最新

场景需求 需要判断是否有新内容更新,确定有更新之后执行pull操作&#xff0c;然后pull成功之后再将新内容进行复制到其他地方 pgit log -1 --prettyformat:"%H" HEAD -- . "origin/HEAD" rgit rev-parse origin/HEAD if [[ $p $r ]];thenecho "Is La…

【java毕业设计】基于ssm+mysql+jsp的大学生兼职信息系统设计与实现(程序源码)-大学生兼职信息系统

基于ssmmysqljsp的大学生兼职信息系统设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于ssmmysqljsp的大学生兼职信息系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方式…

简单理解Linux中的一切皆文件

一款操作系统要管理各种各样不同的硬件&#xff0c;因为硬件的不同所以它们使用的文件系统也不同。但是按道理来说&#xff0c;文件系统的不同对于用户来说可不是一件好事&#xff0c;操作不同的硬件就要使用不同的方法。 但是Linux有一切皆文件。 简单来说&#xff0c;Linux…

React快速入门

最近需要学到react&#xff0c;这里进行一个快速的入门&#xff0c;参考react官网 1.创建和嵌套组件 react的组件封装是个思想&#xff0c;我这里快速演示代码&#xff0c;自己本身也不太熟悉。 代码的路径是src底下的App.js function MyButton() {return (<button>I…

chapter 4 能带理论 energy band

继承自chapter 3 的自由电子模型&#xff1a; 4.1 单电子近似 One electron approximation 列出电子运动的薛定谔方程&#xff1a; E Ψ − ℏ 2 2 m ∇ 2 Ψ U Ψ E \Psi -\frac{\hbar^2}{2m} \nabla^2 \Psi U \Psi EΨ−2mℏ2​∇2ΨUΨ 根据电子在晶体中运动的实际情…

Python编程——列表解析与常用操作

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 一、列表是什么&#xff1f; 二、列表的特点 1、元素…

构建C语言开发环境

有些C/C项目开发周期极长。在处理此类项目过程中&#xff0c;构建开发环境就像施展魔法一样&#xff1a;测试框架被巧妙集成在一起&#xff0c;CI/CD流程将开发者从繁琐重复的工作中解脱出来。 作为程序员&#xff0c;在开发过程中&#xff0c;我只有一个简单的愿望&#xff1…

干翻Dubbo系列第十二篇:Dubbo协议介绍

文章目录 文章说明 一&#xff1a;Dubbo协议 1&#xff1a;Dubbo协议简介 2&#xff1a;Dubbo协议优点 3&#xff1a;Dubbo协议帧的组成 (一)&#xff1a;幻数 (二)&#xff1a;2Way (三)&#xff1a;event (四)&#xff1a;Serilization ID (五)&#xff1a;status …

react之react-redux的介绍、基本使用、获取状态、分发动作、数据流、reducer的分离与合并等

react之react-redux的介绍、基本使用、获取状态、分发动作、数据流、reducer的分离与合并等 一、react-redux介绍二、React-Redux-基本使用三、获取状态useSelector四、分发动作useDispatch五、 Redux 数据流六、代码结构七、ActionType的使用八、Reducer的分离与合并九、购物挣…