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 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.mfbz.cn/a/81969.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vscode如何汉化

首先我们到vscode官网下载 链接如下&#xff1a; Visual Studio Code - Code Editing. Redefined 根据自己需要的版本下载就好 下载并且安装完毕之后 运行vscode 然后按快捷键 CTRLSHIFTX 打开安装扩展界面 搜索简体中文 安装就可以了 谢谢大家观看

聊聊看React和Vue的区别

Vue 更适合小项目&#xff0c;React 更适合大公司大项目&#xff1b; Vue 的学习成本较低&#xff0c;很容易上手&#xff0c;但项目质量不能保证...... 真的是这样吗&#xff1f;借助本篇文章&#xff0c;我们来从一些方面的比较来客观的去看这个问题。 论文档的丰富性 从两个…

kubesphere 集成 sonar

文章目录 安装 helm通过 helm 安装 sonar配置 SonarQube 服务器创建 SonarQube 管理员令牌SonarQube 配置添加到 ks-installer创建 Webhook 服务器将 SonarQube 服务器添加至 Jenkins将 sonarqubeURL 添加到 KubeSphere 控制台重启服务 为新项目创建 SonarQube Token 官方文档&…

Hlang--用Python写个编程语言-函数与基本数据结构实现

文章目录 前言语法表述解析器修改词法解析函数节点函数节点解析List的解析实现解释器节点函数操作String和List处理总结前言 okey,经过一段时间的努力,接下来要实现的是函数。当然还有对应的基本数据结构,那么之后的话,我们的工作就开始进一步转换了。 那么在这块我们要实…

绘制原型图的常用工具之墨刀

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于OA项目的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.墨刀是什么 二.墨刀的作用 三.墨刀界…

【ES6】—使用 const 声明

一、不属于顶层对象window 使用const关键字 声明的变量&#xff0c;不会挂载到window属性上 const a 5 console.log(a) console.log(window.a) // 5 // undefined二、不允许重复声明 使用const关键字不允许重复声明相同的变量 cosnt a 5 cosnt a 6 // Uncaught SyntaxEr…

自然语言处理技术:NLP句法解析树与可视化方法

自然语言处理(Natural Language Processing,NLP)句法解析树是一种表示自然语言句子结构的图形化方式。它帮助将句子中的每个词汇和短语按照语法规则连接起来,形成一个树状结构,以便更好地理解句子的语法结构和含义。句法解析树对于理解句子的句法关系、依存关系以及语义角…

【Android Framework系列】第11章 LayoutInflater源码分析

1 前言 本章节我们主要目目的是了解Activity的xml布局解析、对LayoutInfater源码进行分析。 我们知道Android界面上的每一个控件都是一个个View&#xff0c;但是Android也提供了通过xml文件来进行布局控制&#xff0c;那么xml布局文件如何转成最终的View的呢&#xff1f;转换利…

新版QQ NT 桌面版如何实现内存优化

一、背景 QQ 作为国民级应用,从互联网兴起就一直陪伴着大家,是很多用户刚接触互联网就开始使用的应用。而 QQ 桌面版最近一次技术架构升级还是在移动互联网兴起之前,在多年迭代过程中,QQ 桌面版也积累了不少技术债务,随着业务的发展和技术的进步,当前的架构已经无法很好…

Matplotlib数据可视化(一)

目录 1.Matplotlib简介 2.Matplotlib绘图基础 2.1 创建画布与子图 2.2 添加画布属性 2.3 绘图的保存与显示 1.Matplotlib简介 Matplotlib是一个用于绘制数据可视化图表的Python库。它提供了广泛的功能和灵活性&#xff0c;可以创建各种类型的图表&#xff0c;包括折线图、…

angular中如何定义一个全局组件?

需求&#xff0c;我们需要新建一个navBreadcrumb的全局组件。这是一个面包屑导航&#xff0c;在不同的页面引入时传入一个路由数组即可。 第一步&#xff1a;我们新建这个组件&#xff1a; ng g c navBreadcrumb ng g m navBreadcrumb----------nav-breadcrumb.module-------…

React(6)

1.React插槽 import React, { Component } from react import Child from ./compoent/Childexport default class App extends Component {render() {return (<div><Child><div>App下的div</div></Child></div>)} }import React, { Compon…

机器人制作开源方案 | 送餐机器人

作者&#xff1a;赖志彩、曹柳洲、王恩开、李雪儿、杨玉凯 单位&#xff1a;华北科技学院 指导老师&#xff1a;张伟杰、罗建国 一、作品简介 1. 场景调研 1.1项目目的 近年来&#xff0c;全国多地疫情频发&#xff0c;且其传染性极高&#xff0c;食品接触是传播途径之一。…

Linux-在Ubuntu搭建ftp服务器

By: Ailson Jack Date: 2023.08.20 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/151.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

VScode替换cmd powershell为git bash 终端,并设置为默认

效果图 步骤 1. 解决VScode缺少git bash的问题_failed to start bash - is git-bash.exe on the syst_Rudon滨海渔村的博客-CSDN博客效果解决步骤找到git安装目录下的/bin/bash.exe&#xff0c;复制其绝对路径&#xff0c;例如D:\Program Files\Git\bin\bash.exe把路径的右斜…

小程序-uni-app:hbuildx uni-app 安装 uni-icons 及使用

一、官方文档找到uni-icons uni-app官网 二、下载插件 三、点击“打开HBuildX” 四、选择要安装的项目 五、勾选要安装的插件 六、安装后&#xff0c;项目插件目录 根目录uni_modules目录下增加uni-icons、uni-scss 七、引入组件&#xff0c;使用组件 <uni-icons type&qu…

计算机竞赛 图像识别-人脸识别与疲劳检测 - python opencv

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是…

Unity 之NavMeshAgent 组件(导航和路径寻找的组件)

文章目录 **作用**&#xff1a;**属性和方法**&#xff1a;**用途**&#xff1a;**注意事项**&#xff1a; NavMeshAgent 是Unity引擎中用于导航和路径寻找的组件。它可以使游戏对象在场景中自动找到可行走的路径&#xff0c;并在避免障碍物的情况下移动到目标位置。 以下是关于…

VB6创建ActiveX exe简单方法

VB6创建的COM即可以是线程内的DLL&#xff0c;也可以是线程外独立的EXE&#xff0c;有些32位的旧东西做activex.exe封装后在新硬件新软件x64位上用还是可以的。其实activex DLL和activeX EXE主要功能并不需要改变。activex DLL可以是如下的样子&#xff1a; 一个主工程文件&…

Android OpenCV(七十四): Android OpenCV SDK 升级至 4.8.0

前言 如昨日文章所述,OpenCV 4.8.0 已经发布,虽然系列文章已经停更很久,但是版本升级工作笔者是很乐意快速完成的。 OpenCV 4.8.0 Android SDK:https://github.com/opencv/opencv/releases/download/4.8.0/opencv-4.8.0-android-sdk.zip 更新日志:https://github.com/o…
最新文章