模型
模型类属性命名限制:
- 不能是python的保留关键字
- 不能使用连续的下划线. 这是由django的查询方式决定的
- 定义属性时需要指定字段类型. 通过字段类型的参数指定选项. 语法如下:属性名=models.字段类型(选项)
文件app/models.py
模板格式
from django.db import models
class ModelName(models.Model): # 模型类
Field1 = models.XXXField(...)
Field2 = models.XXXField(...)
...
class Meta: #元选项
db_table = ...
other_metas = ...
模型类
每个模型类都可以被映射为数据库中的一个数据表,类类属性被映射为数据字段,除此之外,数据库表的主键、外键、约束等也通过类属性完成定义
模型类属性
属性 | 描述 |
---|---|
AutoField | 一个自动递增的整型字段,添加记录时会自动增长 AutoField 字段通常会用于充当数据表的主键 若模型没有指定主键字段,则Django 自动添加一个 AutoField字段 |
IntegerField | 用于保存一个整数 |
SmallIntegerField | 类似于 IntegerField 但只具有较小的输入范围,具体范围依赖于所使用的数据库 |
BigIntegerField | 64位整型字段 |
BinaryField | 二进制数据字段 只能通过 types 对其赋值 |
FloatField | 浮点型字段 定义本字段时必须传入 max_digits 和decimal_places 参数 用于定义总位数(不包括小数点和符号)和小数位数 |
DecimalField | 十进制浮点数 max_digits 参数表示总位 decimal_places 参数表示小数位数 |
CommaSeparatedIntegerField | 用于存放逗号分隔的整数值 相较与普通的 CharField, 它有特殊的表单数据验证要求 |
DurationField | 存储时间周期, 用 Python 的 timedelta 类型构建 |
EmailField | 一个带检查 Email 合法性的CharField |
FileField | 一个文件上传字段 在定义本字段时传入参数 upload_to ,用于保存上载文件的服务器文件系统路径 这个路径必须 包含 striftime formatting, 该格式将上载 文件的 date/time 替换 |
FilePathField | 按目录限制规则选择文件 定义本字段必须传入参数 path ,用于限制目录 |
ImageField | 类似于 FileField,同时验证上传对象是否是一个合法的图片 有两个可选参数 height_field 和 width_field 如果提供这两个参数,则图片将按照提供的高度和宽度规格保存 该字段要求安装 Python Imaging 库 |
IPAddressField | 一个字符串形式的IP地址 例如: 192.23.250.2 |
NullBooleanField | 类似于 BooleanField, 但比其多一个None选项 |
PhoneNumberField | 带有美国风格的电话号码校验的 CharField 格式为 XXX-XXX-XXXX |
SlugField | 只包含字母、数字、下划线和连字符的输入字段 通常用于URL |
URL | 用于保存URL |
USStateField | 美国州名的缩写字段,由两个字母组成 |
XMLField | XML字段 是具有XML合法验证的TextField |
属性 | 描述 | 相对应的HTML标签 |
---|---|---|
BooleanField | 布尔字段 | <input type = "checkbox"> |
CharField | 字符串字段,用于较短的字符串 | 单行输入 <input type = "text"> |
TextField | 大容量文本字段 | 多行编辑框 <textarea> |
DateField | 日期字段 | <input type="text"> 、一个JavaScript 日期和一个“today” 快捷按键 有两个额外可选参数: auto_now 当对象被保存时,将该字段的值设置为当前时间 auto_now_add 当对象被首次创建时,将该字段的值设置为当前时间 |
DateTimeField | 类似于 DateField,但同时支持于时间的输入 | |
TimeField | 时间字段,类似于 DateTimeField 但只能表达和输入时间 |
模型类属性参数
参数 | 描述 |
---|---|
max_length | 定义字符的长度 例如 : headline = models.CharField(max_length=255) |
primary_key | 设置一个模型的主键字段 例如: primary_key=true |
null | 定义是否允许相对应的数据库字段为Null, 默认设置为 False 是一个数据库中的非空约束 |
blank | 定义字段是否可以为空 blank 用于字段的HTML表单验证,即判断用户是否可以不输入数据 |
choices | 定义字段的可选值 本字段的值应该是一个包含二维元素的元组 元组的每个元素的第一个值是实际存储的值, 第二个值是HTML页面进行选择时显示的值 |
default | 设置默认值 例如: default="please input here" |
help_text | HTML页面中输入控件的帮助字符串 |
unique | 是否为字段定义数据库的唯一约束 |
无名参数 | 可以设置该字段在HTML页面中的人性化名称 例如:level = models.CharField("请为本条信息评级",max_length=1,choices=LEVELS) 如果不设置本参数,则字段本身将被显示在 HTML 页面中作为输入提示 |
db_index | 若值为True,则在表中会为此字段创建索引 默认False |
db_column | 字段的名称 如果未指定,则使用属性的名称 |
元选项 (Meta类)
通过此类可以定义模型元数据,比如:数据库表名,数据默认排序方式等
属性 | 描述 |
---|---|
abstract: | 标识本类是否为抽象基类 True or False |
app_label: | 定义本类所属的应用 |
db_table: | 映射的数据库表名,例如: db_table=‘moments’ 如果在Meta中不提供 db_table 字段,则Django 会为模型自动生成数据表名,生成的格式为“应用名_模型名”,例如:应用app的模型 Comment 的默认数据表名为 app_comment |
db_tablespace: | 映射的表空间名称 表空间的概念只在某些数据库如 Oracle 中存在,不存在表空间的概念的数据库将忽略此字段 |
default_related_name: | 定义本模型的反向关系引用名称,默认与模型名一致 |
get_latest_by: | 定义按哪个字段值排列以获得模型的开始或结束记录 本属性值通常指向一个日期或整型的模型字段 |
managed: | True or False 定义 Django的 manage.py 命令行工具是否管理本模型。 默认为 True,若将其设置为 False,则运行 python manage.py migrate 时将不会在数据库中生成本模型的数据表,所以需要手工维护数据库的定义 |
order_with_respect_to: | 定义本模型可以按照某外键引用的关系排序 |
order: | 本模型记录的默认排序字段,可以设置多个字段,默认以升序排列, 若以降序排列则表示要在字段名前加负号("-") 例如:定义 user_name 字段升序 和 pub_date 降序排列 order = ['user_name','-pub_date'] |
dafault_permissions: | 模型操作权限 默认为 default_permisstions=('add','change','delete') |
proxy"=: | True or False 本模型及所有继承自本模型的子模型是否为代理模型 |
required_db_features: | 定义底层数据库所必须具备的特性 例如: required_db_features=['gis_enabled'] 只将本数据模型生成在满足 gis_enabled 特性的数据库中 |
required_db_vendor: | 定义底层数据库类型 比如 SQLite,PostgreSQL,MySQL,Oracle 如果定义了本属性,则模型只能在其声明的数据库中被维护 |
unique_together: | 用来设置的不重复的字段组合,必须唯一(可以将多个字段做联合唯一) 例如: unique_together = (("user_name","pub_date"),) 定义了每个 user_name 在同一个 pub_date 中只能有一条数据表记录 因为 unique_together 本身是一个元组,所以可以设置多个这样的唯一约束 |
index_together: | 定义联合索引的字段,可以设置多个 例如: index_together = [["pub_date","deadline"],] |
verbose_name: | 指明一个易于理解和表述的单数形式的对象名称。如果这个值没有被设置,则Django将会使用该model的类型的分词形式作为它的对象表述名,即 CamelCase 将会被转换为camel case |
verbose_name_plural: | 指明一个易于理解和表述的复数形式的对象名称 |
修改默认生成的表名,
#-*- coding:utf-8 -*-
from django.db import models
# Create your models here.
#------------- 一对多 ----------------------
#图书类
class BookInfo(models.Model):
'''图书模型类'''
#id 自动生成
#图书名称
btitle = models.CharField(max_length=20)
#出版日期
bpub_date = models.DateField()
#阅读量
bread = models.IntegerField(default=0)
#评论量
bcomment = models.IntegerField(default=0)
#删除标记
isDelete = models.BooleanField(default=False)
def __str__(self):
# 返回书名
return self.btitle
class Meta:
db_table = 'bookinfo' #指定模型类对应的表明
通过模型操作数据库
查询
模型类.objects.属性
函数名 | 功能 | 返回值 | 说明 |
---|---|---|---|
get | 返回表中满足条件的一条且只能有一条数据 | 返回值是一个模型类对象 | 参数中写查询条件 1. 如果查到多条数据,则抛异常 MultipleObjectsReturned 2. 查询不到数据,则抛异常: DoesNotExist |
all | 返回模型类对应表格中的所有数据 | 返回值是QuerySet类型 | 查询集 |
filter | 返回满足条件的数据 | 返回值是QuerySet类型 | 参数写查询条件 |
exclude | 返回不满足条件的数据 | 返回值是QuerySet类型 | 参数写查询条件 |
order_by | 对查询结果进行排序 | 返回值是QuerySet类型 | 参数中写根据哪些字段进行排序 降序:字段前加 ‘-’号 |
练习与演示
准备练习数据集:使用博客 Django_182_入门指南 图书数据表进行练习
查询集
all
, filter
, exclude
, order_by
调用这些函数会产生一个查询集,QuerySet类对象
可以继续调用上面的所有函数
查询集特性
- 惰性查询:
只有在实际使用查询集中的数据的时候才会发生对数据库的真正查询 - 缓存:
当使用的是同一个查询集时,第一次使用的时候会发生实际数据库的查询,然后把结果缓存起来,之后再使用这个查询集时,使用的是缓存中的结果
限制查询集
3. 可以对一个查询集进行取下标或者切片操作来限制查询集的结果, 下标不允许为负数
4. 对一个查询集进行切片操作会产生一个新的查询集
取出查询集的数据
方法 | 说明 |
---|---|
b[0] | 如果b[0]不存在,抛异常:IndexError |
b[0:1].get() | 如果b[0:1].get() 不存在,抛异常:DoesNotExist |
exists()
函数判断一个查询集是否有数据,返回 True,False
get
查询有id=5的数据
book = BookInfo.objects.get(id=5)
等效
book = BookInfo.objects.get(id__exact=5)
使用查询的id号没有数据,抛的异常
使用get查询出多个数据,抛出异常
all
filter,get,exclude 参数的使用格式
参数格式 模型属性名__条件名=值
判等条件 条件名:exact
查询 id=5 的数据
book = BookInfo.objects.get(id__exact=5)
查询 isDelete=0 的数据
books = BookInfo.objects.filter(isDelete__exact=0)
模糊查询
条件名 contains
包含
查询书名包含 “传” 的图书
books = BookInfo.objects.filter(btitle__contains='传')
条件名 endswith
以…结尾
查询以 “部” 结尾的图书
books = BookInfo.objects.filter(btitle__endswith='部')
条件名 startswith
以…开头
查询 以“流” 开头的图书
books = BookInfo.objects.filter(btitle__startswith='流')
空查询 isnull
查询图书名 不为空 的图书
books = BookInfo.objects.filter(btitle__isnull=False)
查询图书名 为空 的图书
books = BookInfo.objects.filter(btitle__isnull=True)
范围查询 in
查询 id 为 5,7,17 中图书
books = BookInfo.objects.filter(id__in = [5,7,17])
比较查询
gt
(great than) 大于
lt
(less than) 小于
gte
(great than equal) 大于等于
lte
(less than equal) 小于等于
查询 id>0 的图书
books = BookInfo.objects.filter(id__gt = 10)
日期查询
条件名 year
年
查询 1980 年发表的图书
books = BookInfo.objects.filter(bpub_date__year=1980)
条件名 month
月
查询 11月 发表的图书
books = BookInfo.objects.filter(bpub_date__month=11)
查询 1980-1-1 之后 发表的图书
from datetime import date
books = BookInfo.objects.filter(bpub_date__gt=date(1980,1,1))
exclude 不等于
查询 id 不等于 5 的数据
books = BookInfo.objects.exclude(id=5)
order_by 排序
查询所有图书的信息,按照 id 从小到大进行排序
books = BookInfo.objects.all().order_by('id')
查询所有图书的信息,按照 id 从大到小进行排序
books = BookInfo.objects.all().order_by('-id')
把 id>3 的图书信息按照阅读量从大到小排序显示
books = BookInfo.objects.filter(id__gt=3).order_by('-bread')
F对象:类属性之间的比较
查询图书阅读量>评论量的图书
from django.db.models import F
books = BookInfo.objects.filter(bread__gt=F('bcomment'))
查询图书阅读量> 2倍评论量的图书
from django.db.models import F
books = BookInfo.objects.filter(bread__gt=F('bcomment')*2)
Q对象:查询条件之间的逻辑比较
且 &
查询 id>3 并且阅读量 大于30 的图书信息
books = BookInfo.objects.filter(id__gt=3,bread__gt=30)
等效
from django.db.models import Q
books = BookInfo.objects.filter(Q(id__gt=3)&Q(bread__gt=30))
或 |
查询 id>3 或者 阅读量>30 的图书
from django.db.models import Q
books = BookInfo.objects.filter(Q(id__gt=3)|Q(bread__gt=30))
非 ~
查询 id 不等于5 的图书
from django.db.models import Q
books = BookInfo.objects.filter(~Q(id=5))
聚合: 对查询结果进行聚合操作
使用aggregate()
函数来使用聚合,返回是 字典
sum
求和
查询所有图书阅读量的总和
from django.db.models import Sum
bread_num = BookInfo.objects.all().aggregate(Sum('bread'))
count
统计
查询所有图书的数目
from django.db.models import Count
books_num = BookInfo.objects.all().aggregate(Count('id'))
等效
from django.db.models import Count
books_num = BookInfo.objects.aggregate(Count('id'))
count()
函数:统计满足条件的数目,返回的是一个数字
统计所有图书的数目
books_num = BookInfo.objects.all().count()
统计 id>3 的所有图书的数目
books_num = BookInfo.objects.filter(id__gt=3).count()
avg
求平均值
max
求最大值
min
求最小值
插入、更新、删除
调用一个模型类对象save()
方法的时候就可以实现对模型类对应数据表的插入和更新
调用一个模型类对象delete()
方法的时候就可以实现对模型类对应数据表的删除
模型类关系
一对多关系
例如:一本图书 有 多个英雄
models.ForeignKey()
定义在多的类中
在 CTTIC/app/models.py
中
from django.db import models
# Create your models here.
#图书类
class BookInfo(models.Model):
'''图书模型类'''
#id 自动生成
#图书名称
btitle = models.CharField(max_length=20)
#出版日期
bpub_date = models.DateField()
#阅读量
bread = models.IntegerField(default=0)
#评论量
bcomment = models.IntegerField(default=0)
#删除标记
isDelete = models.BooleanField(default=False)
def __str__(self):
# 返回书名
return self.btitle
#英雄人物类
class HeroInfo(models.Model):
#英雄名 hname
hname = models.CharField(max_length=20)
# 性别 hgender,并指定默认值
hgender = models.BooleanField(default=False)
# 备注 hcomment
hcomment = models.CharField(max_length=128)
isDelete = models.BooleanField(default=False)
#关系属性,建立图书类和英雄人物类之间的一对多关系
hbook = models.ForeignKey('BookInfo')
def __str__(self):
return self.hname
关联查询(一对多)
使用博客 Django_182_入门指南 图书数据表进行练习
查询图书信息,要求图书关联的英雄的描述包含‘八’
books = BookInfo.objects.filter(heroinfo__hcomment__contains='八')
查询图书信息,要求图书中的英雄的 id>3
books = BookInfo.objects.filter(heroinfo__id__gt=3)
查询书名为 “天龙八部” 的所有英雄
from app.models import HeroInfo
heros = HeroInfo.objects.filter(hbook__btitle='天龙八部')
总结:
通过多类的条件查询一类的数据
一类名.objects.filter(多类名小写__多类属性名__条件名)
通过一类的条件查询多类的数据
多类名.objects.filter(关联属性__一类属性名__条件名)
多对多关系
例如:一个老师有多个学生,一个学生有多个老师
models.ManyToManyField()
定义在哪个类中都可以
#-*- coding:utf-8 -*-
from django.db import models
#老师信息
class TeacherInfo(models.Model):
#老师姓名
tname = models.CharField(max_length=20)
# 老师描述
tcomment = models.TextField()
# 学生信息
class StudentInfo(models.Model):
# 学生姓名
sname = models.CharField(max_length=20)
# 学生描述
scomment = models.TextField()
teacher = models.ManyToManyField('TeacherInfo')
一对一关系
例如:员工基本信息 对应 员工详细信息
models.OneToOneField()
定义在哪个类中都可以
#-*- coding:utf-8 -*-
from django.db import models
#员工基本信息类
class EmployeeBasicInfo(models.Model):
#姓名
name = models.CharField(max_length=20)
#年龄
age = models.IntegerField()
# 性别,默认False对应男
gender = models.BooleanField(default=False)
def __str__(self):
return self.name
#员工详细信息类
class EmployeeDetailInfo(models.Model):
#联系地址
addr = models.CharField(max_length=255)
#教育经历
education = models.TextField()
#关系属性,代表一对一关系
employee_basic = models.OneToOneField('EmployeeBasicInfo')
def __str__(self):
return self.addr
自关联
自关联是一种特殊的一对多的关系,使用models.ForeignKey()
关联
aParent_id
表示该地区上级所属的id号,若该地区为父级,则为NULL(为空)
#-*- coding:utf-8 -*-
from django.db import models
class AreaInfo(models.Model):
'''地区模型类'''
# 地区名称
atitle = models.CharField(max_length=20)
# 关系属性:代表当前地区的父级地区对应id,允许为空
aParent = models.ForeignKey('self',null=True,blank=True)
执行迁移文件生成表
python manage.py makemigrations
python manage.py migrate
插入测试数据
INSERT INTO app_areainfo VALUES ('110000', '北京市', NULL);
INSERT INTO app_areainfo VALUES ('110100', '北京市', '110000');
INSERT INTO app_areainfo VALUES ('110101', '东城区', '110100');
INSERT INTO app_areainfo VALUES ('110102', '西城区', '110100');
INSERT INTO app_areainfo VALUES ('110103', '朝阳区', '110100');
INSERT INTO app_areainfo VALUES ('110104', '丰台区', '110100');
INSERT INTO app_areainfo VALUES ('110105', '石景山区', '110100');
INSERT INTO app_areainfo VALUES ('110106', '海淀区', '110100');
INSERT INTO app_areainfo VALUES ('110107', '门头沟区', '110100');
此处数据链接
链接:https://pan.baidu.com/s/1EPQ-ztSaB7n5KFGTk98phw
提取码:drye
复制这段内容后打开百度网盘手机App,操作更方便哦
显示 广东 的上级地区 及 下级地区
在 CTTIC/app/views.py
中添加视图函数
#-*- coding:utf-8 -*-
from django.shortcuts import render
from app.models import AreaInfo
def areas(request):
'''获取广州市的上级及下级地区'''
# 获取广州市的信息
area = AreaInfo.objects.get(atitle='广州市')
# 查询广州市的上级地区
parent = area.aParent
# 查询广州市的下级地区
children = area.areainfo_set.all()
context = {
'area':area,
'parent':parent,
'children':children,
}
return render(request,'app/areas.html',context)
新建 CTTIC/templates/app/areas.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自关联案例</title>
</head>
<body>
<h1>当前地区</h1>
{{ area.atitle }} <br/>
<h1>父级地区</h1>
{{ parent.atitle }} <br/>
<h1>下级地区</h1>
<ul>
{% for child in children %}
<li>{{ child.atitle }}</li>
{% endfor %}
</ul>
</body>
</html>
CTTIC/app/urls.py
配置路由
#-*- coding:utf-8 -*-
from django.conf.urls import url
from app import views
urlpatterns = [
# 通过url函数设置url配置项
url(r'^areas$',views.areas),
]
面向对象 ORM
Django模型层 ORM 的一个强大之处是对模型继承的支持,该技术将Python面向对象的编程方法与数据库面向关系表的数据结构有机结合
Django支持三种风格的模型继承
- 抽象类继承:
通过 Meta 中定义属性abstract = True
父类继承models.Model
,但不会在底层数据库中生成相应的数据表
父类的属性列存储在子类的数据表中
将某些字段定义在抽象基类中,免于重复定义这些字段 - 多表继承:
多表继承的每个模型类都在底层数据库中生成相应的数据表管理数据
在多表继承技术中,无论是父表还是子表都会用数据库中相应的数据表维护模型数据,父类中的字段不会重复第在多个子类的相关数据表中进行定义
从某种意义上讲,多表继承才是真正面向对象的ORM技术
多表继承不需要特殊的关键字
在Django内部通过在父类模型和子模型之间建立一对一关系来实现多表继承技术 - 代理模型继承:
通过在子类 Meta 中定义proxy=True
来实现
父类用于在底层数据库中管理数据表
而子类不定义数据列,只定义查询数据集的排序方式等元数据
子类只用于管理父类的数据,而不实际存储数据
使用原因:
是子类中新的特性不会影响父类模型及其已有代码的行为
抽象类继承
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
#父类
class MessageBase(models.Model):
content = models.CharField(max_length=100,null=True)
user_name = models.CharField(max_length=80,null=True)
pub_date = models.DateField(null=True)
class Meta:
abstract = True #定义本类为抽象基类
# 子类
class Moment(MessageBase):
headline = models.CharField(max_length=50)
LEVELS = (
('1','Very good'),
('2','Good'),
('3','Normal'),
('4','Bad'),
)
# 子类
class Comment(MessageBase):
level = models.CharField(max_length=1,choices = LEVELS)
3个类映射到数据库后,会被定义为两个数据表
数据表 moment: 有 id,content,user_name,pub_date,headline 等5个字段
数据表 comment: 有 id,content,user_name,pub_date,level 等5个字段
多表继承
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class MessageBase(models.Model):
content = models.CharField(max_length=100,null=True)
user_name = models.CharField(max_length=80,null=True)
pub_date = models.DateField(null=True)
class Moment(MessageBase):
headline = models.CharField(max_length=50)
class Comment(MessageBase):
level = models.CharField(max_length=1,choices=LEVELS)
生成3个数据表
数据表 MessageBase: 有 id,content,user_name,pub_date 等4个字段
数据表 Moment: 有id,headline等两个字段
数据表 Comment: 有 id,level 等两个字段
代理类继承
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Moment(models.Model):
content = models.CharField(max_length=100,null=True)
user_name = models.CharField(max_length=80,null=True)
pub_date = models.DateField(null=True)
headline = models.CharField(max_length=50)
class OrderedMoment(Moment):
class Meta:
proxy = True;
ordering = ["-pub_date"]