04 Django 模型基础

一、models 字段类型

django 根据属性的类型确定以下信息:
当前选择的数据库支持字段的类型
渲染管理表单时使用的默认 html 控件
在管理站点最低限度的验证

注意django 会自动为表添加自动增长的主键列,每个模型只能有一个主键列。如果使用选项设置某属性为主键列后,django 则不会再生成默认的主键列。

属性命名限制

定义属性

定义属性时,需要的字段类型被定义在 django.db.models.fields 目录下。为了方便使用,通常会导入到 django.db.models 包,使用 models.Field类型名 定义字段。

from django.db import models

class UserModels(models.Model):
	name = models.CharField(max_length=30)
	# 省略后续代码

逻辑删除和物理删除

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

is_delete = models.BooleanField(default=False)

常用字段类型

二、常用字段参数

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

null=True:数据库中字段是否可以为空

blank=TruedjangoAdmin 中添加数据时是否可允许空值

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

primary_key=True:主键,对 AutoField 设置主键后,就会代替原来的自增 id

auto_now :自动创建。无论添加或修改,都是当前操作的时间

auto_add_now:自动创建。永远都是创建时的时间

choices:后参 admin 下拉菜单

USER_TYPE_LIST = (
	(1, "超级用户"), 
	(2, "普通用户"),
)

user_type = models.IntegerField(choices=USER_TPYE_LIST, default=1, verbose_name="用户类型")

max_length:最大长度

default:默认值

verbose_nameadmin 中字段的显示名称,后台显示的名称

name|db_column :数据库中的字段名称

unique=True:不允许重复

db_index=True:数据库索引。例如:如果你想通过 name 查询的更快的话,给他设置为索引即可

editable=True:在 Admin 里是否可以编辑,不可编辑则不显示

设置表名

class Meta:
	db_tale = "person"

三、models 基本操作

一般数据库操作流程:

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

Django 通过 Model 操作数据库,不管数据的类型是 mysql 还是 sqliteDjango 自动生成相应数据库类型的 SQL 语句,所以不需要关注 SQL 语句和类型,对数据的操作 Django 自动完成,只要写 Model 就可以了。

Django 使用对象关系映射(Object Relational Mapping, ORM)框架去操控数据库。

ORM 对象关系映射是一种程序技术,用于实现面向对象编程语言中不同类型系统的数据之间的转换。

增删改查

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="zhang", last_name="san")

# 4. 使用 get_or_create 方法,可以防止重复
Author.objects.get_or_create(first_name="zhang", last_name="san")

基本操作之

# 使用 Queryset 的 delete 方法
# 删除指定条件的数据
Author.objects.filter(first_name="zhang").delete()

# 删除所有数据
Author.objects.all().delete()

# 注意:objects 不能直接调用 delete 方法

# 使用模型对象的 delete 方法
obj = Author.objects.get(id=5)
obj.delete()

基本操作之

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 更新时会更新所有字段,如果只想更新某个字段,减少数据库操作,提高效率,
# 可以使用 save 函数的 update_fields 参数指定更新的字段
obj.first_name = "li"
obj.save(update_fields=["first_name"])  # update_fields 叁数指定更新字段

基本操作之

# 基础操作
# get() 获取单条数据,只能是一个
Author.objects.get(id=123) 
# get() 如果没有找到符合条件的对象,会引发模型类 .DoesNotExist 异常,如果找到多个,会引发模型
# .MultipleObjectsReturned 异常

# first() 返回查询集 Queryset 中的第一个对象
# last() 返回查询集中的最后一个对象
# count() 返回当前查询集中的对象个数
# exits() 判断查询集中是否有数据,如果有数据返回 True 没有 False

# all() 获取全部数据
Author.objects.all()

# values() 获取指定列的值,可以传多个参数。返回包含字典的列表(保存了字段名和对应的值)
Author.objects.all().values("password")

# values_list() 获取指定列的值,可以传多个参数。返回包含元组的列表(只保存值)
Author.objects.all().values_list("password")



# 进阶操作
# 获取个数
Author.objects.filter(name="seven").count()

# __gt 大于
Author.objects.filter(id__gt=1)  # 获取 id 大于 1 的值
# select * from Author where id > 1

# __gte 大于等于
Author.objects.filter(id__gte=1)  # 获取 id 大于等于 1 的值 
# select * from Author where id >= 1

# __lt 小于
Author.objects.filter(id__lt=10)  # 获取 id 小于 10 的值
# select * fro Author where id < 10

# __lte 小于等于
Author.objects.filter(id__lte=10)  # 获取 id 小于等于 10 的值
# select * from Author where id <= 10

# 连续区间,大于小的,小于大的
Author.objects.filter(id__qt=1, id__lt=10)  # 获取 id 大于 1 且小于 10 的值
# select * from Author where id > 1 and id < 10

# __in 在某个集合中
Author.objects.filter(id__in=[11, 22, 33])  # 获取 id 在 11, 22, 33 中的数据
# select * from Author wehre id in (11, 22, 33)


# exclude 与 in 搭配使用,不在某个集合中,想当于 not in
# 注意:in 是 filter 的一个参数,而 exclude 是 objects 的方法
Author.objects.exclude(id__in=[11, 22, 33])
# select * from Author where id not in (11, 22, 33)


# __contains 包含子字符串
Author.objects.filter(name__contains="ven")  # 与 sql 中的 like 相同
# select * from Author where name like "%ven%"

# __icontains 忽略大小写
Author.objects.filter(name__icontains="ven")  

Author.objects.filter(name__regex="^ven")  # 正则匹配
Author.objects.filter(name__iregex="^ven")  # 正则匹配,忽略大小写

# __range 区间范围 between and 
Author.objects.filter(age__range=[10, 20])

# __startwidth, __istartwidth 以什么开始,i 忽略大小写
# __endwithd, __iendwidth 以什么结尾,i 忽略大小写
# 上面不带 i 的也忽略大小写


# 排序
# 按 id 升序排
Author.objects.filter(name="seven").order_by("id")  
# 按 id 降序排
Author.objects.filter(name="seven").order_by("-id")

# 切片,分页时用
Author.objects.all()[10: 20]

# 查所有数据
Author.objects.all()  # 可以
Author.objects.filter()  # filter 没有查询条件,也可以查所有

聚合操作

聚合函数:MaxMinSum

需要先导入聚合函数才能使用

from django.db import Max, Min, Sum  # 导入
# ...

result = PersonModel.objects.aggregate(Max('age'))  # 最大值, {"age__max": 666}
result = PersonModel.objects.aggregate(Min('age'))  # 最小值, {"age__min": 100}
result = PersonModel.objects.aggregate(Avg('age'))  # 平均值, {"age__avg": 194.34}
result = PersonModel.objects.aggregate(Count('age'))  # 计数, {"age__count": 6}
result = PersonModel.objects.aggregate(Sum('age'))  # 求和, {"age__sum": 1166}

排序

# 升序, 按 age 列升序排列
persons = PersonModel.objects.all().order_by("age")

# 升序,先按 age 列升序排列,如果 age 列有相等的记录,则这些记录再按 id 列升序徘列  
persons = PersonModel.objects.all().order_by("age", "id")  

# 降序排列,按 id 列降序排列
persons = PersonModel.objects.all().order_by("-id")  

分页之手动分页

分页功能,也就是第几页显示第几页的数据,如第 3 页显示第 3 页的数据(这句好像是费话)。

为什么当点击前端的第几页时会显示第几页的数据?后台传给前端的有两个数据,一个是第几页(即页数),另一个是第几页对应的数据。

需要以下几个变量

第几页,用 for page in range(1, 总页数) 循环即可。但是,模板中不能用 range(1, 总页数)。所以需要先在后台 view 中处理,再传到前端。

# 总页数 = 数据总数 / 每页显示的数据个数 并向上取整
# python 向上取整的函数在 math 包中 math.ceil()

total_data = PersonModel.objects.count()
total_page = math.ceil(total_data / per_page)

page_range = range(1, total_page+1)

page_range 传到前端,在前端用 for page in page_range 循环,page 就是第几页

上面说过了,需要两个参数,第一个参数有了(第几页,这里就是 page,第二个参数(第 page 页显示的数据)怎么得?

第几页 数据范围 数据下标范围 切片
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=4 31 ~ 40 30 ~ 39 [30: 40]
... ... ... ...
page=n ... ... [(n-1)*10: n*10]
page=page ... ... [(page-1)*per_page: page*per_page]
pageper_page=(page1)per_page+per_page=pageper_pageper_page+per_page=pageper_page 

对所有数据(查询集)进行切片可得当前页(第几页)的数据。

def paginate(request, page=1):
	per_page = 10
	persons = PersonModels.objects.all()
	persons = person[(page-1)*per_page: page*per_page]  # 第 page 页显示的数据

persons 就是第 page 页显示的内容。将 pagepersons 传到前端,就可以准备处理分页了。


data = {
	"page_range": page_range,
	"persons": persons,
}
return render(request, "paginate.html", data)

还需要写路由

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

page 变量传递,需要写带参数的路由,即 <int:page>。对应的 paginate 视图函数写的什么参数,路由的参数就要写什么。如,def paginate(request, page=1) 用的是 page,路由的参数也得写 page,即 <int:page>

因为前端点击页码跳转后显示对应的数据,有反向解析,所以需要在 path 中指定 name 参数

前端

    <ul class="btns">  
        {% for page in page_range %}  
            <li>  
	            {# 反向解析 #}
                <a href="{% url 'paginate' page %}">  
                    <button>{{ page }}</button>  
                </a>            
            </li>       
        {% endfor %}  
    </ul>  
  
    <hr>    
    <ul>        
	    {% for person in persons %}  
            <li>{{ person.name }} - {{ person.age }}</li>  
        {% endfor %}  
  
    </ul>

分页之自动分页

自动分页就是使用 django.core.paginator 包中的 Paginator 对象,自动得到 第几页第几页显示的数据 两个变量。

from django.core.paginator import Paginator

def paginate2(request, page=1):
	per_page = 10
	all_data = PersonModel.object.all()

	paginator = Paginator(all_data, per_page)
	persons = paginator.page(page)
	pages = paginator.page_range

	data = {
		"persons": persons,
		"pages": pages,
	}

	return render(request, "paginate2.html", data)