[Python] Django框架入门2——深入模型
说明:
本文主要深入了解模型(models.py),涉及ORM简介、模型定义、模型成员、模型查询、自连接等。需要一定基础,可以先走一走基本入门流程。
附录一使用mysql数据库,附录二Django开发流程。
目录:
一、ORM简介
ORM简介
二、模型定义
1、基本模型
2、字段类型
3、字段选项
4、关系
5、元选项
三、模型成员
1、查询
2、Django默认的管理器
3、自定义管理器
四、模型查询
1、查询集
2、字段查询
3、
五、自连接
附录一:使用mysql
附录二:Django开发流程
一、ORM简介(引用,详情请查阅相关文档)
ORM(Object Relational Mapping)即是"对象-关系-映射"的简称,是MVC框架一个重要的部分(Django也支持ORM),它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置可以轻松更换数据库。
ORM的作用:
根据对象的类型生成表结构
将对象、列表的操作,转换成sql语句
将sql查询到的结果转换为对象、列表
使用ORM主要方便开发人员,极大减轻开发人员的工作量,避免手写sql语句出现的各种问题。
Django中的模型包含存储的字段和约束,对应着数据库中唯一的表。
二、模型定义
1、基本的模型
1 from django.db import models 2 3 4 class BookInfo(models.Model): 5 """ 6 图书类 => 图书表(booktest_bookinfo) 7 """ 8 # 图书标题 => varchar(20) 9 btitle = models.CharField(max_length=20) 10 # 发布日期 => datetime 11 bpub_date = models.DateTimeField() 12 # 阅读数 => int(11) 13 bread = models.IntegerField(default=0) 14 # 评论数 => int(11) 15 bcomment = models.IntegerField(default=0) 16 # 是否删除 => tinyint 17 isDelete = models.BooleanField(default=False) 18 19 def __str__(self): 20 return self.btitle 21 22 23 class HeroInfo(models.Model): 24 """ 25 英雄类 => 英雄表(booktest_heroinfo) 26 """ 27 # 英雄名字 => varchar(20) 28 hname = models.CharField(max_length=20) 29 # 性别 => tinyint 30 hgender = models.BooleanField(default=True) 31 # 简介 => varchar(1000) 32 hcontent = models.CharField(max_length=1000) 33 # 是否删除 => tinyint 34 isDelete = models.BooleanField(default=False) 35 # 外键 => int(11) 36 hbook = models.ForeignKey(BookInfo) # 外键
说明:
定义属性时,需要字段类型
字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中
使用方式:见上面代码
对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False
2、字段类型(引用)
表单控制是指在django管理站点的前端显示。
AutoField:根据ID自动增长的IntegerField,一般无需指定,django会自动添加到模型中,数据库类型为 int(11)。
BooleanFidld:true/false字段,此字段的默认表单控件是CheckboxInput,数据库类型为 tinyint。
NullBooleanField:支持null、true、false三种值,数据库类型为 tinyint。
CharField(max_length=字符长度):字符串,默认的表单控件是TextInput,数据库类型为 varchar(字符长度)。
TextField:大文本字段,一般字符长度超过4k使用,默认的表单控件是Textarea,数据库类型为 varchar(字符长度)。
IntegerField:整数,数据库类型为 int。
DecimalField(max_digits=None, decimal_places=None):使用python的Decimal实例表示的十进制浮点数,数据库类型为 decimal(P, D):
DecimalField.max_digits:位数总数
DecimalField.decimal_places:小数点后的数字位数。
FloatField:使用python的float实例来标识的浮点数。
DateField([auto_now=False, auto_noew_add=False]):使用pyhton的datetime.date实例表示日期。
参数auto_now:每次保存对象时,自动设置该字段为当前时间,用于保存"最后一次修改"的时间戳,他总是使用当前的日期,默认为false
参数auto_now_add:当对象第一次被创建时自动设置当前时间,用于保存创建的时间戳,它总是使用当前日期,默认为false
该字段默认对应的表单控件是TextInput
auto_now、auto_now_add、default 这些设置是相互排斥的,他们之间任何组合将会发生错误的结果 。
TimeField:使用python的datetime.time实例表示的时间,参数同DatEField。
DateTimeField:使用python的datetime.datetime实例表示的日期和时间,参数同DateField
FileField:一个上传文件的字段,一般不会把文件保存在数据库中,建议保存文件上传的路径。
ImageField:继承FieldField的所有属性和方法,当对上传的对象进行校验,确保上传是有些的image。
3、字段选项
使用:title = models.CharFidle(null=True)
null:如果为True,Django将空值以NULL存储到数据库中,默认值是False。
blank:如果为True,则该字段允许为空,默认值是False。
null与blank对比:null是数据库范畴的概念,blank是表单验证范畴的。
db_column:字段的名称,如果为指定,则使用属性的名称。
db_index:如果为True,则在表中会为此字段创建索引
default:默认值。
primary_key:如果为True,则该字段会成为模型的主键字段。
unique:如果为True,表示这个字段在表中必须有唯一值。
4、关系
关系的类型有如下:
ForeignKey:一对多,将字段定义在多的端中(如上面基本模型的代码)。
ManyToManyField:多对多,将字段定义在两端中。
OneToOneField:一对一,将字段定义在任意一端中。
访问方式:
用一访问多:对象.模型类小写_set
bookinfo.heroinfo_set
用一访问一:对象.模型类小写
heroinfo.bookinfo
heroinfo.book_id
5、元选项
在模型类中定义类Meta,用于设置元信息
1 class BookInfo(models.Model): 2 # 元选项 3 class Meta: 4 # 数据表名 5 db_table = 'bookinfo' 6 # 对象默认排序字段,正序 7 ordering = ['id'] 8 # ordering = ['-id'] # 倒序
三、模型成员
1、查询数据
book_list = BookInfo.objects.all()
2、Django默认的管理器
objects:是Manager类型的对象,用于与数据库进行交互。
当定义模型时没有指定管理器,则Django会为模型类提供一个名为objects的管理器。
支持明确指定管理器(如果自己指定manager,django不再为模型类生成名为objects的默认管理器):
1 class BookInfo(models.Model):
2 manager1 = models.Manager()
3、自定义管理器
管理器是Django的模型进行数据库的查询操作的接口,Django应用的每个模型都拥有至少一个管理器。
自定义管理器主要用于以下两种情况:
情况一:修改管理器返回的原始查询集:重写get_query_set()方法。
情况二:向管理器中添加额外的方法
代码如下(manager2就是自定义的管理器):
1 class BookInfoManager(models.Manager):
2 """
3 自定义管理器
4 """
5 # 情况一:更改默认查询结果,只查未删除的数据
6 def get_queryset(self):
7 return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
8
9 # 情况二:定义模型类的创建方法
10 def create(cls, btitle, bpub_date):
11 b = BookInfo()
12 b.btitle = btitle
13 b.bpub_date = bpub_date
14 b.bread = 0
15 b.bcomment = 0
16 b.isDelete = False
17 return b
18
19
20 class BookInfo(models.Model):
21 btitle = models.CharField(max_length=20)
22 bpub_date = models.DateTimeField()
23 bread = models.IntegerField(default=0)
24 bcomment = models.IntegerField(default=0)
25 isDelete = models.BooleanField(default=False)
26
27 def __str__(self):
28 return self.btitle
29
30 # 管理器
31 manager1 = models.Manager()
32 manager2 = BookInfoManager()
情况一的测试结果如下:
情况二的测试结果如下(简化创建对象的过程:new->赋值->save):
四、模型查询
1、查询集
在管理器上调用过滤器方法返回查询集。
查询集经过过滤筛选后返回信的查询集,因此可以写成链式过滤。
惰性执行:创建查询集不会带来任何数据的访问,直到调用数据的时候(迭代、序列号、与if合用),才会访问数据库。
返回查询集的方法,称为过滤器:all()、filter()、exclude()、order_by()、values()
写法:manager.filter(键1=值1, 键2=值2) => manager.filter(键1=值1).filter(键2=值2)
返回单个值的方法:
get():返回单个满足添加的对象
如果未找到会引发"模型类 DoesNotExist"异常
如果多条被返回,会引发"模型类 MultipleObjectsReturn"异常
count():返回当前查询的总条数
first():返回第一个对象
last():返回最后一个对象
exists():判断查询集中是否有数据,有则返回True
限制查询集:
查询集返回列表,可以使用下标的方式进行限制,等同于sql中limit和offset子句
注意:不支持负数索引
如果下标返回一个新的查询集,不会立刻执行查询
如果获取一个对象,直接使用[0],等同于[0:1].get()。但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常。
查询集的缓存
每个查询集都包含一个缓存来最小化对数据库的访问。
在新建的查询集中,缓存为空,首次对查询集求值时,会发生数据库查询,Django会将查询的结果存在查询集的缓存中,并返回请求的结果,接下来对查询集求值将重用缓存的结果。
情况一:构建两个查询集,无法重用缓存,每次查询都会与数据库进行交互,增加数据库的负载
情况二:两次循环使用同一个查询集,第二次使用缓存中的数据
何时查询集不会被缓存:当只对查询集的部分进行求值时,会检查缓存,但是如果这部分不再缓存中,那么接下来查询返回的几率将不会被缓存,这意味着使用索引来限制查询集将不会填充缓存,如果这部分数据已经被缓存,则直接使用缓存中的数据。
1 query = BookInfo.objects.all()
2 for each in query # 缓存
3 for each in query[0,10] # 不缓存
4 # 使用子集不缓存
2、字段查询:比较运算符,F对象,Q对象
实现where子句,作为方法filter()、exclude()、get()的参数。
语法:属性名__比较运算符=值,__ 表示两个下划线,左侧是属性名称,右侧是比较类型。
对于外键:使用 "属性名__id" 表示外键原始值
转义:like语句中使用%与_,匹配数据中的%与_,在过滤器中直接写:
filter(title__contains="%") => WHERE `title` LIKE '%\%%'
比较运算符
exact:表示判等,大小写敏感,如果没有写 “比较运算符”,表示判等:
filter(isDelete=False) => WHERE `isDelete` = 0
contains:是否包含。大小写敏感:
exclude(btitle__contains='传') => WHERE `btitle` LIKE '%传%'
startswhit、endswith:以value开头或结尾,大小写敏感:
exclude(btitle__endswith='传') => WHERE `btitle` LIKE '%传'
exclude(btitle__startswith='传') => WHERE `btitle` LIKE '传%'
isnull、isnotnull:是否为null:
filter(btitle__isnull=False) => WHERE `btitle` != null
在前面加 i 表示不区分大小写,如:iexact、icontains、istartswith、iendswith
in:是否包含在范围内:
filter(pk__in=[1,2,3]) => WHERE `id` IN (1, 2, 3)
gt、gte、lt、lte:大于、大于等于、小于、小于等于
filter(id__gt=3) => WHERE `id` > 3
filter(id__lte=5) => WHERE `id` <= 5
year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算:
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
跨关系的查询:处理join查询:
filter(heroinfo__hcontent__contains='八') => SELECT * FROM `bookinfo` JOIN `heroinfo` ON `bookinfo`.`id`=`heroinfo`.`hbook` WHERE `heroinfo`.`hcontent` LIKE '%八%'
聚合函数:
使用aggregate()函数返回聚合函数的值
函数:Avg、Count、Max、Min、Sum
from django.db.models import Max
maxDate = BookInfo.objects.aggregate(Max('bpub_date'))
count的一般用法:count = list.count()
F对象:构建等号右侧字段:
可以使用模型的字段A与字段B进行比较,如果A在等号左侧,则B出现在等号的右侧时需要通过F对象构造:
list.filter(bread__gt=F('bcomment')) => WHERE `bread` > `bcomment`
Django支持对F对象使用算数运算:
list.filter(bread__gte=F('bcomment')) => WHERE `bread` >= `bcomment` * 3
F()对象还可以写作“模型类__列名”进行关联查询:
list.filter(isDelete=F('heroinfo__isDelete')) => WHERE `bookinfo`.`isDelete` = `heroinfo`.`isDelete`
对于date/time字段,可与timedelta()进行运算:
list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))
Q对象:处理逻辑或:
过滤器方法中关键字参数查询,会合并为And进行查询。
需要进行or查询,使用Q对象
Q对象(django.db.models.Q)用于封装一组关键字参数,这些关键字参数与“比较运算符”中的相同
from django.db.models import Q
list.filter(Q(pk__lt=6))
Q对象可以使用&(and)、|(or)操作符组合起来
当操作符应用在两个Q对象时,会产生一个新的Q对象
list.filter(pk__lt=6).filter(bcomment__gt=10) # WHERE `id` < 6 AND `bcomment` > 10
list.filter(Q(pk__lt=6) | Q(bcomment__gt=10)) # WHERE `id` < 6 OR `bcomment` > 10
使用~(not)操作符在Q对象前表示取反
list.filter(~Q(pk__lt=6))
可以使用&|~结合括号进行分组,构造复杂的Q对象
过滤函数可以传递一个或多个Q对象作为位置参数,如果有多个Q对象,这些参数的逻辑为and
过滤器函数可以混合使用Q对象和关键字参数,所有参数都将And在一起,Q对象必须位于关键字参数的前面。
五、自连接
自连接主要应用于无限级分类等。
1 class AreaInfo(models.Model):
2 atitle = models.CharFields(max_lenth=20)
3 aParent = mdoels.ForeignKey('self', null=True, blank=True)
访问关联对象:
上级:area.aParent
下级:area.areainfo_set.all()
附录一:使用mysql数据库
1、安装插件:python-mysql 或 mysqlclient
我的电脑安装的是python3.5,通过wheel安装mysqlclient,下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient
2、修改配置文件
附录二:Django开发流程
1、创建项目:执行指令:django-admin startproject [项目名]
2、进入项目目录,创建应用:执行指令:python manager.py startapp [应用名]
3、进入应用目录,在models.py中定义模型类,要求继承自models.Model
4、把应用加入settings.py文件中的installed_app项中
5、生成迁移文件,执行指令:python manager.py makemigrations
6、执行迁移生成表,执行指令:python manager.py migrate
7、使用模型类进行crud操作
*、使用数据库生成模型类
python manager.py inspectdb > booktest/models.py