Django模型学习
模型使用流程
- 配置数据库
- 定义模型类
- 激活模型
- 使用模型
数据库配置
Django默认使⽤的是sqlite,但在⽣产环境中⼀般会⽤mysql、postgrsql、oracle 等关系型数据库。
在开发环境中,安装mysql的数据库驱动mysqlclient,mysqlclient是连接mysql的驱动,不能提供数据库服务,需要单独安装mysql。
1 | pip install mysqlclient |
在项⽬的 settings.py ⽂件中找到 DATABASES 配置项,将其信息修改为:
1 | DATABASES = { |
ORM
对象关系映射(Oject Relational Mapping,简称ORM)模式是⼀种为了解决⾯向 对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使⽤描 述对象和数据库之间映射的元数据,⾃动⽣成sql语句,将程序中的对象⾃动保存 到关系数据库中。
优点:
- 隐藏了数据库访问的细节,简化了sql的使⽤,提⾼了开发效率
- 解耦业务逻辑层(view)和数据处理层(model),简化了开发流程,提⾼了 系统的可移植性
- 提⾼了安全性
优点:
- 执⾏效率低
- 对复杂sql⽆能为⼒
- 增加了学习成本
基本概念
⾯向对象概念 | ⾯向关系概念 |
---|---|
类 | 表 |
对象 | 记录(一行) |
属性 | 字段(属性,列) |
- 一个模型类对应一个表
- 每个模型都必须继承django.db.models.Model
模型属性
模型中的属性和数据库表的字段对应,必须定义。模型的属性需要定义成类属性
1 | #属性定义语法为: |
- 属性命名规则
- 不能是python的保留关键字
- 不允许使⽤连续的下划线,因为连续下划线在查询中会⽤到
- 定义属性时需要指定字段类型
- 主键⼀般不⽤⾃⼰定义,django会⾃动创建⾃增⻓主键列,如果你⾃⼰定 义了主键,则django不会再⾃动⽣成主键
字段类型
字段名称 | 字段说明 | 参数 |
---|---|---|
AutoField | ⼀个根据实际Id⾃动增⻓的 IntegerField(通常不指定 ⾃动 ⽣成) | primary_key:bool 是否为主键 |
CharField | 字符串,默认的表单样式是 TextInput | max_length=字符⻓ 度 |
TextField | ⼤⽂本字段,⼀般超过4000使 ⽤,默认的表单控件是 Textarea | |
IntegerField | 整数 | |
DecimalField | 使⽤python的Decimal实例表 示的⼗进制浮点数 | |
FloatField | ⽤Python的float实例来表示的 浮点数 | |
BooleanField | true/false 字段,此字段的默认 表单控制是CheckboxInput | |
NullBooleanField | ⽀持null、true、false三种值 | |
DateField | 使⽤Python的datetime.date实 例表示的⽇期,该字段默认对 应的表单控件是⼀个TextInput | auto_now和 auto_now_add、 default这三个参数 不能同时共存 |
TimeField | 使⽤Python的datetime.time实 例表示的时间 | 参数同DateField |
DateTimeField | 使⽤Python的 datetime.datetime实例表示的 ⽇期和时间 | 参数同DateField |
ImageField | 继承了FileField的所有属性和⽅ 法,但对上传的对象进⾏校 验,确保它是个有效的image |
- auto_now: 每次保存对象时,⾃动设置该字段为当前时间,⽤于”最后⼀次修 改”的时间戳,它总是使⽤当前⽇期,默认为false
- auto_now_add: 当对象第⼀次被创建时⾃动设置当前时间,⽤于创建的时间 戳,它总是使⽤当前⽇期,默认为false
字段选项
适⽤于任何字段,可以实现字段的约束,在⽣成字段时通过⽅法的关键字参数指 定。
可选参数 | 说明 |
---|---|
null | 如果 True ,Django将 NULL 在数据库中存储空值。默认 是 False 。不要在字符串字段上使⽤。null是数据库范畴的概 念 |
blank | 如果 True ,该字段允许为空。默认是 False 。同null不同, 如果字段有 blank=True ,则表单验证将允许输⼊空值。如果 字段有 blank=False ,则需要该字段。 |
db_column | ⽤于此字段的数据库列的名称。如果没有给出,Django将使 ⽤该字段的名称。 |
db_index | 如果 True ,将为此字段创建数据库常规索引。 |
unique | 如果 True ,该字段在整个表格中必须是唯⼀的。 |
primary_key | 如果 True ,此字段是模型的主键。 |
default | 默认值 当前字段如果不给值则执⾏默认值 |
定义模型
我们可以在应⽤的models.py中定义模型:
1 | from django.db import models |
- 数据库的表名等于:应⽤名_模型名,如果想指定表名,可以在Meta中使⽤ db_table指定
- 如果没有指定主键,Django将⾃动给表创建⼀个⾃增⻓主键,名为id
1 | id = models.AutoField(primary_key=True) |
- Meta中常⽤设置:
名称 | 说明 |
---|---|
db_table | 数据库中的表名 |
abstract | 当设置为True时,说明当前模型是抽象基类 |
managed | 如果设置False,则迁移不会创建或删除表,默认是True |
ordering | ⽤于记录排序,ordering = [‘pub_date’]或降序ordering = [‘- pub_date’] |
激活模型
- 创建迁移⽂件 (此刻表并没有创建到库中)
1 | $ python manage.py makemigrations |
- 执⾏迁移 (将模型创建到库中)
1 | $ python manage.py migrate |
然后在应⽤的migrations⽬录中应该⽣成了迁移⽂件
1 | ├── app |
注意:任何对字段或表的修改都需要重新迁移
- 反向迁移 可以根据数据库中表⾃动创建模型
1 | python manage.py inspectdb > App/models.py |
模型的使用
我们可以在交互式Python shel环境中,使⽤Django提供的免费API。要调⽤ Python shell,请使⽤以下命令:
1 | $ py -3 manage.py shell |
增删改
1 | def sql(request): |
数据的逻辑删除
对于重要数据,⼀般不会直接删除,会在表中增加⼀个字段⽐如: is_deleted,如果删除的话,将这个字段置为True,以后查询的时候不在查 询,这种操作称为逻辑删除
数据查询
过滤器查询
要从数据库检索数据,⾸先要获取⼀个查询集(QuerySet),查询集表示从数据库获
取的对象集合,它可以有零个,⼀个或多个过滤器。返回查询集的⽅法,称为过滤
器,过滤器根据给定的参数缩⼩查询结果范围,相当于sql语句中where或limit。
- 在管理器上调⽤过滤器⽅法会返回查询集
- 查询集经过过滤器筛选后返回新的查询集,因此可以写成链式过滤
- 惰性执行:创建查询集不会带来任何数据库的访问,直到调⽤数据时,才会访问数据库
- 以下对查询集求值:迭代、切⽚、序列化、与if合⽤、 repr()/print()/len()/list()/bool()
遍历数据库所有数据:
1 | def user_list(request): |
1 |
|
过来取可串联使用,比如:users = User.objects.filter(username='Mxm7788', password='1').filter(id=5)
实现mysql limit功能显示前10条:User.objects.all()[:10]
实现mysql limit功能显示5-10条:User.objects.all()[4:11]
注意:Python切片包头不包尾
管理器的⽅法 | 返回类型 | 说明 |
---|---|---|
模型类.objects.all() | QuerySet | 返回表中所有数据 |
模型类.objects.filter() | QuerySet | 返回符合条件的数据,相当于where |
模型 类.objects.exclude() | QuerySet | 返回不符合条件的数据 |
模型 类.objects.order_by() | QuerySet | 对查询结果集进⾏排序 |
模型 类.objects.values() | QuerySet | 返回⼀个Queryset,其中每个对象为⼀个字典 |
模型 类.objects.values_list() | QuerySet | 和values()基本相同,但每个对象是⼀个元组 |
模型 类.objects.reverse() | QuerySet | 对排序的结果反转 |
模型类.objects.only(字段) | QuerySet | 只显示指定字段 |
模型 类.objects.defer(字段) | QuerySet | 去除指定字段 |
模型类.objects.get() | 模型对象 | 返回⼀个满⾜条件的对象; 如果没有找到符合条件的对象,会引发模 型类.DoesNotExist异常; 如果找到多个,会引发模型 类.MultiObjectsReturned 异常 |
模型类.objects.first() | 模型对象 | 返回第⼀条数据 |
模型类.objects.last() | 模型对象 | 返回最后⼀条数据 |
模型 类.objects.earliest() | 模型对象 | 根据指定字段返回最早增加的记录 |
模型 类.objects.latest(field) | 模型对象 | 根据field字段返回最近增加记录 |
模型类.objects.exists() | bool | 判断查询的数据是否存在 |
模型类.objects.count() | int | 返回查询集中对象的数⽬ |
非过滤器查询
.get非过滤器查询只能返回一条数据,返回一条或不返回都会报错。
查询结果集中记录数:
1 | # 返回类型必须是QuerySet才能调用count |
查询结果集中是否有记录:
1 | # 开发很常用,用于避免错误处理 |
filter查询
相当于sql语句中where⼦句,在传参是无法使用关系运算符,它可以为filter、exclude和get⽅法提供参数。
基础语法:
属性名称__⽐较运算符=值 #是两个下划线
filter支持多个条件查询,使用英文逗号分割多个条件:
1 | myarticle.objects.filter(id__lt=50, id__gte=20) |
操作符 | 含义 | |
---|---|---|
=或exact | 精确判等 | name=’admin’ |
iexact | 不区分⼤⼩写判等 | name__iexact=’Admin’ |
gt | 大于 | uid__gt=5 |
gte | 大于等于 | uid__gte=5 |
lt | 小于 | uid__lt=5 |
lte | 小于等于 | uid__lte=5 |
contains | 模糊查询,等价like ‘% 值%’ | name__contains=’Ad’ |
icontains | 不区分⼤⼩写的模糊查 询 | name__icontains=’ad’ |
startswith | 以什么开头 | name__startswith=’a’ |
istartswith | 不区分⼤⼩写的以什么开头 | name__istartswith=’A’ |
endswith | 以什么结尾 | name__endswith=’n’ |
iendswith | 不区分⼤⼩写的以什么结尾 | name__iendswith=’N’ |
isnull | 判空,是否为空(等价 = None) | name__isnull=True |
in | 包含 | uid__in = [1,2,3] #in后⾯必须是可迭 代对象 |
regex | 正则匹配 | uname__regex= r’^a |
iregex | 不区分大小写的正则匹配 | uname__iregex= r’^a |
统计查询
需要先导⼊模块:
1 | from django.db.models import Max,Min,Sum,Avg,Count |
- 聚合查询:对多⾏查询结果的⼀列进⾏操作
1 | #统计记录总数: select count(*) from user |
- 分组统计
1 | #等价sql: select type,count(*) from user group by type |
Q对象和F对象
需要先导⼊模块:
1 | from django.db.models import Q,F |
- Q对象可以对关键字参数进⾏封装,从⽽更好的应⽤多个查询,可以组合& (and)、|(or)、~(not)操作符。
1 | #原⽣sql:select * from user where uid = 2 or uid = 3 |
- F对象:⽤于⽐较表中两个字段
1 | #等价sql:select * from user where uid < type |
原始sql
您可以使⽤它 模块类.objects.raw()来执⾏原始查询并返回模型实例,或者您可以完全避免模型层并直接执⾏⾃定义SQL。
使用原始sql语句可以执行任何sql语句(必须包含主键),和模型无关。
- raw执行sql语句
1 | users = User.objects.raw("select * from user") |
- ⾃定义sql
1 | from django.db import connection |
模型成员
模型类和数据库中表对应,模型类的对象和记录对象,模型类本身没有数据库访问 功能,但模型类中有⼀个Manager类的对象,通过管理器对象可以实现和数据库的 访问。
当我们没有为模型类定义管理器时,Django会为模型类⽣成⼀个名为objects的管 理器,⾃定义管理器后,Django不再⽣成默认管理器objects。
管理器是Django的模型进⾏数据库操作的接⼝,Django应⽤的每个模型都拥有⾄ 少⼀个管理器。Django⽀持⾃定义管理器类,继承⾃models.Manager。
⾃定义管理器类主要⽤于两种情况:
- 修改原始查询集
- 向管理器类中添加额外的⽅法,如向数据库中插⼊数据。
重命名管理器
在模型类中⾃定义⼀个新的管理器,则原有的objects管理器不会存在,以后对数 据库的操作使⽤⾃⼰定义的管理器
1 | #模型类 |
自定义管理器
- 修改原始查询集(由all()获取的查询集)
- 修改管理器的get_queryset⽅法,可以改变all⽅法返回的原始查询集(实现自动过滤)
1 | #⾸先⾃定义Manager的⼦类 |
- 给管理器类中添加额外的⽅法
1 | class ArticleManager(models.Manager): |
模型对应关系
关系数据库最强⼤大的地⽅方在于“关系”,也即表和表之间是有关联的,这种关联有三 种类型:
- 一对一
- 一对多
- 多对多
一个学⽣生有⼀一个档案,一个档案属于一个学⽣生,那么学⽣生表和档案表就是一对一关 系。学生表是主表,档案表是从表,从表中有一个外键和学生表关联,并且要求外键取值唯一。对应关键字为:OneToOneField
一对一
- 创建模型
1 | class Student(models.Model): |
- 添加数据
1 | def addstudent(request): |
- 删除数据
1 | def deletestudent(request): |
- 正向查询
1 | def findstudent(request): |
- 反向查询
1 | def findarchives(request): |
- 跨关系查询
1 | def lookup(request): |
- on_delete
- CASECADE 默认,默认级联删除数据
- PROTECT 保护模式,当从表中存在级联记录的时候,删除主表记录会抛 出保护异常,从表中不存在级联数据的时候,是允许删除的
- SET_XXX
- NULL 外键字段本身必须允许为空
- DEFAULT 外键字段本身有默认值
- DO_NOTHING 什么都不做
一对多
⼀个出版社可以出版多本书,⼀本书只能被⼀个出版社出版。出版社和图书表属于 ⼀对多,⼀对多⼀般将主表中的主键并到从表中做外键。在模型中⽤ForeignKey表 示多对⼀
1 | class Publisher(models.Model): |
多对多
⼀个买家可以购买多件商品,⼀件商品可以被多个买家购买,买家和商品之间构成 多对多关系,多对多关系必然会⽣成⼀张中间表:买家-商品表,记录商品和买家 的关系,该表包含商品表主键和买家表的主键
1 | from django.db import models |
模型继承
django中的数据库模块提供了⼀个非常不错的功能,就是⽀持models的面向对 象,可以在models中添加Meta,指定是否抽象,然后进⾏继承。父类的嵌套类中有一个abstract参数,这个参数告诉Django怎么操作数据库。
当abstract为False时:DJango会创建两个表,一个子表一个父表,子表不会只有子类里的定义的属性
当abstract为True时:Django只会创建子表,不会创建父类表,生成表列为父类属性+子类属性
1 | class Animal(models.Model): |
分页
Paginator 分页器
分页是把数据按照一页多少个输出,Paginator⽤于分⻚,但Paginator并不具体管理具体的⻚的处理,而是使⽤Page 对象管理具体页面。
- 创建分⻚器对象
1 | paginator = Paginator(users, 10) #paginator是返回对象,Paginator是Django提供的分页管理器,需要导入 |
- 返回对象的属性
- 对象.count 分页对象的个数
- 对象.num_pages 总页数
- 对象.page_range 页码的列表
- ⽅法
- page(num) 返回page对象 如果给定的页码不存在 则抛出异常
page 对象
page对象是调用创建分页管理器返回的对象的方法返回的对象,具体负责每页的处理,包括每页的数据,当前页的页码,是否有上⼀⻚ 或下⼀页等。
类别 | 名称 | 说明 |
---|---|---|
属性 | object_list | 当前页码上的所有数据(QuerySet) |
属性 | number | 当前页码值 |
属性 | paginator | 返回Paginator的对象 |
方法 | has_next | 是否有下⼀页 |
方法 | has_previous | 是否有上⼀页 |
方法 | has_other_pages | 是否有上⼀页或者下⼀页 |
方法 | next_page_number | 返回下⼀页的页码 |
方法 | previous_page_number | 返回上⼀页的页码 |
方法 | len | 返回当前页数据的个数 |
实例
- 视图代码
1 | def page(request, page=1): |
- 模板代码
1 |
|
Django 系列文章
Django基础学习:https://www.xpctf.cn/posts/24c5/
Django晋级学习:https://www.xpctf.cn/posts/2f99/
Django模型学习:https://www.xpctf.cn/posts/d0b4/
Django会话学习:https://www.xpctf.cn/posts/7490/
Django Form学习:正在写