[Python] Django框架入门2——深入模型

时间:2021-07-16 10:35:47

说明:

  本文主要深入了解模型(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中的模型包含存储的字段和约束,对应着数据库中唯一的表。

  [Python] Django框架入门2——深入模型

二、模型定义

  1、基本的模型  

 from django.db import models

 class BookInfo(models.Model):
"""
图书类 => 图书表(booktest_bookinfo)
"""
# 图书标题 => varchar(20)
btitle = models.CharField(max_length=20)
# 发布日期 => datetime
bpub_date = models.DateTimeField()
# 阅读数 => int(11)
bread = models.IntegerField(default=0)
# 评论数 => int(11)
bcomment = models.IntegerField(default=0)
# 是否删除 => tinyint
isDelete = models.BooleanField(default=False) def __str__(self):
return self.btitle class HeroInfo(models.Model):
"""
英雄类 => 英雄表(booktest_heroinfo)
"""
# 英雄名字 => varchar(20)
hname = models.CharField(max_length=20)
# 性别 => tinyint
hgender = models.BooleanField(default=True)
# 简介 => varchar(1000)
hcontent = models.CharField(max_length=1000)
# 是否删除 => tinyint
isDelete = models.BooleanField(default=False)
# 外键 => int(11)
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,用于设置元信息    

 class BookInfo(models.Model):
# 元选项
class Meta:
# 数据表名
db_table = 'bookinfo'
# 对象默认排序字段,正序
ordering = ['id']
# ordering = ['-id'] # 倒序

三、模型成员

  1、查询数据

     book_list = BookInfo.objects.all()

  

  2、Django默认的管理器

    objects:是Manager类型的对象,用于与数据库进行交互。

    当定义模型时没有指定管理器,则Django会为模型类提供一个名为objects的管理器。

    支持明确指定管理器(如果自己指定manager,django不再为模型类生成名为objects的默认管理器):   

 class BookInfo(models.Model):
manager1 = models.Manager()

  3、自定义管理器

    管理器是Django的模型进行数据库的查询操作的接口,Django应用的每个模型都拥有至少一个管理器。

    自定义管理器主要用于以下两种情况:

      情况一:修改管理器返回的原始查询集:重写get_query_set()方法。

      情况二:向管理器中添加额外的方法

    代码如下(manager2就是自定义的管理器):

 class BookInfoManager(models.Manager):
"""
自定义管理器
"""
# 情况一:更改默认查询结果,只查未删除的数据
def get_queryset(self):
return super(BookInfoManager, self).get_queryset().filter(isDelete=False) # 情况二:定义模型类的创建方法
def create(cls, btitle, bpub_date):
b = BookInfo()
b.btitle = btitle
b.bpub_date = bpub_date
b.bread = 0
b.bcomment = 0
b.isDelete = False
return b class BookInfo(models.Model):
btitle = models.CharField(max_length=20)
bpub_date = models.DateTimeField()
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False) def __str__(self):
return self.btitle # 管理器
manager1 = models.Manager()
manager2 = BookInfoManager()

    情况一的测试结果如下:

      [Python] Django框架入门2——深入模型

        [Python] Django框架入门2——深入模型

    情况二的测试结果如下(简化创建对象的过程:new->赋值->save):

      [Python] Django框架入门2——深入模型

        [Python] Django框架入门2——深入模型

四、模型查询

  1、查询集

    在管理器上调用过滤器方法返回查询集。

    查询集经过过滤筛选后返回信的查询集,因此可以写成链式过滤。

    惰性执行:创建查询集不会带来任何数据的访问,直到调用数据的时候(迭代、序列号、与if合用),才会访问数据库。

    返回查询集的方法,称为过滤器:all()、filter()、exclude()、order_by()、values()

    写法:manager.filter(键1=值1, 键2=值2) => manager.filter(键1=值1).filter(键2=值2)

      [Python] Django框架入门2——深入模型

    返回单个值的方法:

      get():返回单个满足添加的对象

        如果未找到会引发"模型类 DoesNotExist"异常

        如果多条被返回,会引发"模型类 MultipleObjectsReturn"异常

      count():返回当前查询的总条数

      first():返回第一个对象

      last():返回最后一个对象

      exists():判断查询集中是否有数据,有则返回True

    

    限制查询集:

      查询集返回列表,可以使用下标的方式进行限制,等同于sql中limit和offset子句

      注意:不支持负数索引

      如果下标返回一个新的查询集,不会立刻执行查询

      如果获取一个对象,直接使用[0],等同于[0:1].get()。但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常。

      

    查询集的缓存

      每个查询集都包含一个缓存来最小化对数据库的访问。

      在新建的查询集中,缓存为空,首次对查询集求值时,会发生数据库查询,Django会将查询的结果存在查询集的缓存中,并返回请求的结果,接下来对查询集求值将重用缓存的结果。

      情况一:构建两个查询集,无法重用缓存,每次查询都会与数据库进行交互,增加数据库的负载

      [Python] Django框架入门2——深入模型

      情况二:两次循环使用同一个查询集,第二次使用缓存中的数据

        [Python] Django框架入门2——深入模型

      何时查询集不会被缓存:当只对查询集的部分进行求值时,会检查缓存,但是如果这部分不再缓存中,那么接下来查询返回的几率将不会被缓存,这意味着使用索引来限制查询集将不会填充缓存,如果这部分数据已经被缓存,则直接使用缓存中的数据。

 query = BookInfo.objects.all()
for each in query # 缓存
for each in query[0,10] # 不缓存
# 使用子集不缓存

  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对象必须位于关键字参数的前面。

五、自连接

  自连接主要应用于无限级分类等。

 class AreaInfo(models.Model):
atitle = models.CharFields(max_lenth=20)
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、修改配置文件

      [Python] Django框架入门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