Django模型层的扩展

创建一个Django工程及app

PS D:\test> django-admin startproject modelExplore 
PS D:\test> cd modelExplore 
PS D:\test\modelExplore> python manage.py startapp modelExample 
PS D:\test\modelExplore>

添加app 到settings.py文件中的INSTALLED_APPS配置项中

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'modelExample',
]

配置数据库

使用默认的数据库:sqlite3

数据库的配置方法我在 [Django介绍与框架整合,并使用MySQL实现增删改查] 这篇文章中有过介绍。

模型

Django为了对各种数据库提供支持,因此抽象出一种模型的概念,并提供一套统一的调用API。这样你就可以根据业务需求去选择数据库,而不需要面对因数据库的变更而修改代码,极大地减轻了开发人员的工作量。

在学习模型前,我们先了解ORM(Object Relational Mapping:对象关系映射)的概念:

用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。简单理解,就是根据对象的类型生成表结构,将对象、列表操作转换为sql语句,将sql语句查询到的结果转换为对象、列表。

模型实现的就是一种对象关系映射,那么模型、属性、表、字段他们又有哪些不为人知的秘密呢?

  • 一个模型类在数据库中对应一张表
  • 在模型类中定义的属性对应模型对照表中的一个字段。

Django根据属性的类型确定以下信息。

  • 当前选择的数据库支持字段的类型
  • 渲染管理表单时使用的默认html控件
  • 在管理站点最低限度的验证

属性命名限制

  • 不能是Python的保留关键字(遵循标识符规则)
  • 由于Django的查询方式,不允许使用连续的下划线

定义模型

我们在modelExample这个目录下的models.py定义模型,定义模型需要引入django.db中的models,模型类要继承models.Model类,一个模型就是一个类。定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用,我们将整个django.db.models导入,我们以创建一个简单的用户表为例:

from django.db import models 


# Create your models here. 

class User(models.Model): 
    acocunt = models.CharField(max_length=20) 
    name = models.CharField(max_length=20) 
    passwd = models.CharField(max_length=16) 
    email = models.CharField(max_length=50) 
    sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),),default=0)

说明:不需要定义主键,在生成表时自动添加。

关系

分类:

  • ForeignKey:一对多,将字段定义在多的端中
  • ManyToOneField:多对多,将字段定义在两端中
  • OneToOneField:一对一,将字段定义在任意一端中

用一访问多

  • 格式:对象.模型类小写_set
  • 示例:grade.students_set

用一访问一

  • 格式:对象.模型类小写
  • 示例:grade.students

访问id

  • 格式:对象.属性_id
  • 示例:student.sgrade_id

属性类型

  • AutoField:一个根据实际ID自动增长的IntegerField,通常不指定,如果不指定一个主键字段将自动添加到模型中

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

  • CharField(max_length=字符长度):字符创,默认的表单样式是TextInput
  • TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea
  • IntegerField:整数
  • DecimalField(max_digits=None, decimal_places=None):

使用Python的Decimal实例表示的十进制的浮点数 参数说明: DecimalField.max_digits:位数总数 DecimalField.decimal_places:小数点后的数字位数

  • FloatField:用Python的float实例来表示的浮点数
  • BooleanField:True/False字段,此字段的默认表单控制是CheckboxInput
  • NullBooleanField:支持null.true,false三种字段
  • DateField([auto_now=False,auto_now_add=False]):

使用Python的datetime.date实例表示的日期 参数说明: DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于“最后一次修改“的时间戳,他总是使用当前日期,默认为False DateField.auto_now_add:当前对象第一次被创建时设置当前时间,用于创建的时间戳,他总是使用当前日期,默认为False 说明: 该字段默认对应的表单控件是一个TextInput。在管理员站点添加了一个JavaScript写的日历控件,和一个Today的快捷按钮,包含了一个额外的invalid_date错误信息键 注意: Auto_now_add,auto_now and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果。

  • TimeField:使用Python的datetime.time实例表示的时间,参数同DateField

  • DateTimeField:使用Python的datetime,datetime实例表示的日期和时间,参数同DateField

  • FileField:一个上传文件的字段

  • ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保他是个有效的image

  • 外键ForeignKey

from django.db import models 

class Grades(models.Model): 
    class_name = models.CharField(max_length=20) 
    headteacher = models.CharField(max_length=20) 
    students_count = models.IntegerField(2)

class student(models.Model): 
    student_num = models.CharField(max_length=20)
    student_name = models.CharField(max_length=20) 
    student_sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),),default=0)
    student_birth = models.DateField(auto_now_add=true)
    student_grade = models.ForeignKey('Grades')

属性选项

  1. 通过属性选项,可以实现对属性的约束
  2. 在属性对象通过关键字参数指定
  • Null:如果为True,Django将控制以null存储到数据库中,默认值是False

  • Blank:如果为True,则该字段允许为空白,默认值是False

    注意:null是数据库范畴的概念,blank是表单验证范畴的

  • Db_column:字段的名称,如果未指定,则使用属性的名称

  • Db_index:若值为True,则在表中会为此字段创建索引

  • Default:默认值

  • Primary_key:若为True,则该字段会成为模型的主键字段

  • Unique:如果为True,这个字段在表中必须有唯一值

元选项:在模型类定义中定义Meta类,用于设置元信息

Class Stucent(models.Model):
    # someField
    Class Meta:
        db_table = 'stucents' # 定义数据表名,推荐使用小写字母,如果不写,数据表名默认为项目名小写_类名小写
        ordering=['id']  # 对象的默认排序字段,获取对象的列表时使用。
        # ordering = ['id']:升序;ordering = ['-id']:降序
        # 注意:排序会增加数据库的开销。

生成迁移文件并执行迁移

在以往的开发模式中,我们通常都是设计好数据库表后,将其创建出来,然后通过sql来进行查询,而在Django中,我们可以省去数据库表的设计和创建步骤,我们定义模型其实就是对表的设计,执行迁移就是创建数据库表。修改模型后只需要再迁移即可

第一步:生成迁移文件

在我们的工程目录下执行以下命令:python manage.py makemigrations

PS D:\modelExplore> python manage.py makemigrations 
Database version : 5.5.53 
Migrations for 'firstApp': firstApp\migrations\0001_initial.py 
- Create model User 
PS D:\modelExplore>

如果控制台会打印出类似以上的内容,此时会在modelExample app目录下的migrations目录下生成一个迁移文件,但是现在还没有在数据库中创建表。

第二步:执行迁移

在执行完上一步后,只是生成了一个迁移文件,此时还没有在数据库中创建表,我们需要执行以下命令来生成数据表:python manage.py migrate

PS D:\modelExplore> python manage.py migrate 
Database version : 5.5.53 
Operations to perform: Apply all migrations: admin, auth, contenttypes, firstApp, sessions Running migrations: 
Applying firstApp.0001_initial... OK 
PS D:\modelExplore>

执行以上的命令相当于执行SQL语句创建了数据表。看到类似于上文的结果表示成功,可以去数据库中查看是否已经创建了这张表。表名是你的appName_modelName连接在一起的,当然了,表名也是可以自定义的(在元选项中有过说明)。

模型成员

类属性: objects,他是Manager类型的一个对象,作用是与数据库进行交互。当定义模型类时,没有指定管理器则Django默认为模型创建一个名为objects的管理器。

自定义模型管理器:

class student(models.Model): 

    stuObj = models.Manager() # 自定义模型管理器,此时再去使用objects就会报错了

    student_num = models.CharField(max_length=20)
    student_name = models.CharField(max_length=20) 
    student_sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),),default=0)
    student_birth = models.DateField(auto_now_add=true)
    student_grade = models.ForeignKey('Grades')

当模型指定模型管理器。Django就不在为模型类生成objects模型管理器了。模型管理器可以创建多个。

自定义管理器Manager类: 模型管理器是Django的模型进行与数据库进行交互的接口,一个模型类可以有多个模型管理器

作用:

  • 向管理器中添加额外的方法
  • 修改管理器返回的原始查询集----重写get_queryset()方法。

实例:

Class StudentsManager(models.Manager): 
    get_queryset(self): 
        return super(StudentsManager,self).getqueryset().filter(isDelete=False)


class student(models.Model): 

    # 为对象创建一个模型管理器
    stuObj = objects_new = StudnetsManager()

    student_num = models.CharField(max_length=20)
    student_name = models.CharField(max_length=20) 
    student_sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),),default=0)
    student_birth = models.DateField(auto_now_add=true)
    student_grade = models.ForeignKey('Grades')

创建对象:

目的:向数据库中添加数据 当创建对象时,Django不会对数据库进行读写操作,当调用save()方法时才与数据库交互,将对象保存到数据库。

注意:__init__方法已经在基类(父类)中使用,在自定义的模型中无法使用

方法:

  • 在模型类中增加一个类方法
  • 在自定义管理器中添加一个方法

此处乱七八糟的呢

@classmethod 
def createStudents(cls,name,age , gender, contend…): 
    student = cls(sname = name , sage = age…) 
    return student 

stu.createStudents(…) 

# 在自定义管理器中添加一个方法 在StudentsManager中创建一个方法 
def createStudents(self,name,age , gender, contend…): 
    # 可以直接创建一个对象,但是这种方式不通用,限制了这个管理器。 
    stu = Students()

# 在通常情况下,我们使用以下的方法创建,这样什么对象调用就是什么对象 
stu2 = self.model() 
stu2.sname = name … Print(type(stu2)) 
student = cls(sname = name , sage = age…) 
return student stu.obj.createStudents
# 定义一个类方法创建对象,此方法要创建在Students类中

模型查询

查询集:

查询集是表示从数据库中获取的对象的集合,查询集可以有多个过滤器,过滤器就是一个函数,基于所给的参数限制查询集结果。从sql角度来说,查询集合select语句等价,过滤器就像where条件。

1在管理器上调用过滤器方法返回结果集
2查询集经过过滤器筛选后返回新的查询集,所以我们可以写成链式调用
3惰性执行:创建查询集不会带来任何数据库的访问,直到调数据时,才会访问数据库
4直接访问数据的情况:
迭代:
序列化:
与if合用:
5返回查询集的方法称为过滤器
all():返回查询集中的所有数据
filter():返回符合条件的数据 :

  • filter(键=值);

  • filter(键=值,键=值);

  • filter(键=值).filter(键=值)

  • 多条件是and查询

    exclude():过滤掉符合条件的数据
    order_by():排序
    values():一条数据就是一个对象(字典),返回一个列表
    6返回单个数据
    get():返回一个满足条件的对象
    注意:如果没有找到符合条件的对象,会引发‘模型类DoesNotExist’异常
    如果找到了多个对象,也会引发‘模型类MultipleObjectsReturned’异常
    count():返回当前查询集中的对象个数
    first():返回当前查询集中的第一个对象
    last():返回当前查询集中的最后一个对象
    exists():判断查询集中是否有数据,如果有返回True,否则返回False
    7限制查询集:查询集返回列表,可以使用下标的方法进行限制,等同于SQL中的limits
    students.objects.all()[0:5]

    注意下标不能是负数

    实现分页功能

8查询集的缓存
概述:每个查询集都包含一个缓存,来最小化的对数据库访问
在新建的查询集中,缓存首次为空,第一次对查询集求值,会发生数据缓存。,django会将查询出来的数据做缓存,并返回查询结构,以后的查询直接使用查询缓存的方式查询。
9字段查询
概述:
1实现了SQL中的where语句作为方法filter()、exclude()、get()的参数
2语法:属性名称比较运算符=值
3外键:属性名id
4转义:类似SQL中的like语句中使用%为了匹配占位。
例:filter(sname__contains=”%”)
比较运算符:
1exact判断,大小写敏感,例filter(isDelete=False)
2contailns:是否包含,大小写敏感:例:student.objects.filter(sname__contains=’于’)
3startswith/endswith:以value开头或结尾,大小写敏感
以上四个在前面加上一个I,就表示不区分大小写:iexact/icontains/istartswith
4isnull/isnotnull :是否为空;filter(sname__isnull=False)
5in : 是否包含。filter(pk__in=[2,4,6,8,])
6gt/gte/lt/lte : 大于/大于等于/小于/小于等于;filter(sage__gt=50)
7year/month/day/week_day/hour/minute/second : 日期 filter(last_time__year = 2018)
8跨关联查询:
处理join语句:
语法:模型类名
属性名
_比较运算符
# 描述中带有小白兔关键字的数据属于哪个班级
Grades.objects.filter(Students__scontend__contains=’小白兔’)
9快速查询:pk:代表的主键
聚合函数:使用aggregate()函数返回聚合函数的值
1Avg:
2Count
3Max/Min/Sum
from django.db.model import Max
Students.objects.aggregate(Max(‘sage’))
F对象:可以使用模型的A属性与B属性进行比较
from django.db.model import F
Grades.objects.filter(ggirlnum__gt=F(‘gboynum’) + 20)
支持F对象算数运算和时间运算
Q对象
概述:过滤器的方法中的关键字参数,条件为and方式
需求:进行or查询
解决:使用Q对象
from django.db.model import Q
Students.objects.filter(Q(pk__lt=3) | Q(sage__gt=50))
# 只有一个Q的时候,与没有Q相同
Students.objects.filter(Q(pk__lt=3))
# 在Q前加一个~,表示取反
Students.objects.filter(~Q(pk__lt=3))

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容