Django-C002-深入模型,到底有多深

此文章完成度【100%】留着以后忘记的回顾。多写多练多思考,我会努力写出有意思的demo,如果知识点有错误、误导,欢迎大家在评论处写下你的感想或者纠错。

 

 

ORM介绍:对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 。从效果上说,它其实是创建了一个可在编程语言里使用的--“虚拟对象数据库”。

面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。

那么说说他的好处:

  • 实现了数据模型与数据库的解耦,通过简单的配置就可以轻松更换数据库,而不需要修改代码

  • 只需要面向对象编程,不需要面向数据库编写代码

  • 在MVC中model中定义的类,通过ORM与关系型数据库中的表对应,对象的属性提现对象之间的关系,这种关系也被映射到数据表中

 

那么SQLite 并不是我喜欢的数据库,虽然自带,还是不感冒,所以下面就看看我们如何使用自己想用到的数据库之MySQL

重新创建一个项目,来用于接下来的练习

【Django version】: 2.1

【pymysql version】:0.9.3

【python version】: 3.7

 1.首先找到test2/settings.py 里的有关数据库设置的变量 变量名=DATABASES

# 你可以选择将原有指定的数据库注释掉,也可以直接删除
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.mysql', # 使用的引擎为mysql
'NAME': 'school', # 数据库名称
'USER': 'root', # 数据库登录名
'PASSWORD': 'toor', # 数据库密码
'HOST': 'localhost', # 指定的数据库所在的主机
'PORT': 3306, # 数据库使用的端口
}
}

※踩了一晚上坑,终于pymysql可以成功导入,Django2.2.X的版本貌似不兼容pymysql,将版本降到2.1之后

如果你的pycharm没有安装pymysql,你在导入的时候将会有红色下标,这时只需要在Pycharm中的Settings里面找到Project:你的项目名 里面添加即可

# 接下来运行,成功返回下面的页面,不成功你可以在评论中提出你的问题,咱们一起想解决办法
python manage.py runserver

通过之前的学习我们可以看出Django的M,是为了简化程序与数据库之间的操作,可以让我们用更少的代码实现更多的功能,这也是Python的编程风格,我们只需要通过类和对象,比如之前例子中的ClassInfo、StudentInfo这两个类和他们的对象,就能完成很多数据库的增、删、改、查。

我们还是需要两个类来进行演示:ClassInfo 【班级类】StudentInfo【学生类

这里需要注意的是我们创建类的时候必须要继承models.Model类

首先我们找到models.py把这两个类创建出来

 1 from django.db import models
 2 
 3 
 4 class ClassInfo(models.Model):
 5     """班级模型类"""
 6     name = models.CharField(max_length=20)  # 班级名
 7     class_date = models.DateField()  # 开班日期
 8     class_size = models.IntegerField(default=0)  # 班级人数 默认0
 9     isDelete = models.BooleanField(default=False)  # 逻辑删除 默认False
10 
11 
12 class StudentInfo(models.Model):
13     """学生模型类"""
14     name = models.CharField(max_length=20)  # 姓名 最大长度20
15     age = models.IntegerField()  # 年龄
16     gender = models.BooleanField(default=False)  # 性别 默认 False 男
17     interest = models.CharField(max_length=200)  # 爱好 最大长度200
18     isDelete = models.BooleanField(default=False)  # 逻辑删除 默认False
19     # 关联的班级 班级与学生是一对多的关系 所以定义在学生类中
20     the_class = models.ForeignKey(ClassInfo, on_delete=models.CASCADE)

 models.py里面的类已经创建完,之后需要生成迁移文件

python manage.py makemigrations

 

python manage.py migrate

 

# 首先进入到这个表里
use school
# 接下来我们检查一下刚刚生成的数据表
show tables;

 既然已经完成让我们看一下表结构:

desc school_classinfo;
desc school_studentinfo;

 

那么现在我们添加一些数据到数据库中

# 班级
insert
into school_classinfo(name,class_date,class_size,isDelete) values ('python class','2019-6-21',17,0), ('java class','2019-6-21',1,0), ('c#','2019-6-21',1,0), ('php','2019-6-21',1,0);
#  学生
insert into school_studentinfo(name,age,gender,interest,isDelete,the_class_id) values
('蒙奇·D·路飞',19,0,'所有美食首先是肉、喜欢探险、感兴趣于新奇怪异的事物',0,1),
('罗罗诺亚·索隆',21,0,'睡觉、修炼、喝酒',0,1),
('娜美',20,1,'钱,橘子',0,1),
('乌索普',19,0,'发明各种东西、制造武器',0,1),
('山治',21,0,'下厨,抽烟,浪漫幻想',0,1),
('托尼托尼·乔巴',17,1,'医术',0,1),
('妮可·罗宾',30,1,'考古',0,1),
('弗兰奇',36,0,'汉堡包',0,1),
('布鲁克',90,0,'演奏,喝红茶,牛奶,说骷髅冷笑话',0,1),
('甚平',46,0,'不详',0,1),
('红发香克斯',46,0,'红发海贼团船长,原罗杰海贼团的实习员,拥有强大的霸王色霸气',0,1),
('乔拉可尔·米霍克',46,0,'世界第一大剑豪,红发好友,索隆的师傅',0,1),
('巴索罗米·熊',46,0,'原革命军干部,肉球果实能力者,已被贝加庞克改造成完全改造为“人间兵器”和平主义者PX-0,失去作为人的记忆',0,1),
('波雅·汉库克',32,1,'路飞',0,1),
('特拉法尔加·罗',26,0,'红心海贼团船长,手术果实能力者,极恶的世代之一',0,2),
('蒙奇·D·龙',42,0,'革命军总司令,被政府认定为世界最凶恶的罪犯',0,3),
('唐吉诃德·多弗朗明哥',36,0,'王位',0,4);

 

模型类数据已经都就位了,接下来我们去定义视图找到应用下的views.py

from django.shortcuts import render, redirect
from school.models import ClassInfo
from datetime import date


# 查询所有班级并显示
def index(request):
    class_list = ClassInfo.objects.all()
    return render(request, 'school/index.html', {'class_list': class_list})


# 创建新的班级类
def create(request):
    c = ClassInfo()
    c.name = "海贼团 class"
    c.class_date = date(2019, 6, 21)
    c.class_size = 0
    c.isDelete = 0
    c.save()
    return redirect('/index.html')


# 逻辑删除指定编号的图书
def delete(request, cid):
    c = ClassInfo().objects.get(id=cid)
    c.isDelete = 1
    c.save()
    return redirect('/index.html')

为了让程序能找到我们的视图,配置URLconf就显得尤为重要了,那么显示在test2下的urls.py

# test2下的urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^', include("school.urls"))
]

等价于

# test2下的urls.py

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include("school.urls"))
]

项目下的配置完,去应用下也就【school/urls【1】】配置具体对应的视图函数

   【1】:这里的urls.py是我们自己创建的,用于配置我们自己的视图,可以更好的进行路径管理

from django.conf.urls import url
from school import views
urlpatterns = [
    url(r'index$', views.index),
    url(r'create$', views.create),
    url(r'delete(d+)$', views.delete),
]

M和V已经完成,现在就差一个T了,那么就去创建模板,在创建模板之前先去settings.py检查一下TEMPLATES里面是否已经配置好

 

下面就是模板index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>school</title>
    <style>
        a{text-decoration:none}
    </style>
</head>
<body>
    <h1 style="text-align: center">Circle & School</h1>
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10>
    <ul style="list-style-type:none">
        {% for class in class_list %}
            <li style="text-align: center">
                {% if class.isDelete != 1 %}
                    <a href="#">{{ class.name }}</a>------------<a href="/delete{{class.id}}">删除</a></li>
                {% endif %}
            </li>
        {% endfor %}

    </ul>
    <a href="/create" style="padding-left: 120px;font-size: 30px">创建</a>
</body>
</html>

 这里开始测试一下,是否成功,那么我们将服务器运行起来

python manage.py runserver

 测试创建

测试删除

这里只是逻辑上的删除,并没有在数据库真正的删除,有一个好处是避免我们误操作,也可以保存数据,供以后研究

定义属性

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

  • 当前选择的数据库使用字段的类型

  • 渲染管理表单时使用的默认HTML控件

  • 在管理站点最低限度的验证

django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。

属性命名限制:

  • 不能是python的保留关键字。

  • 不允许使用连续的下划线,这是由django的查询方式决定的,在字段查询的说明会讲解为什么不能使用连续下划线

  • 定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下  

    • 属性 = models.字段类型(选项)

字段类型:使用时需要导入django.db.models包

  • AutoField:自动增长的IntegerField,默认不需要写,不写会自动创建属性名为id的自动增长属性
  • BooleanField:布尔字段,值 True、False
  • NullBooleanField:值为 Null、True、False
  • CharField:字符串
    • 参数max_length表示最大字符个数
  • TextField:大文本字段 一般超过4000个字符使用
  • IntegerField:整数
  • DecimalField:十进制浮点数
    • 参数max_digits表示总位数。
    • 参数decimal_places表示小数的位数
  • FloatField:浮点数
  • DateField[auto_now=False, auto_now_add=False]):日期。
    • 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false。
    • 参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false。
    • 参数auto_now_add和auto_now是相互排斥的,组合将会发生错误。
  • TimeField:时间,参数同DateField。
  • DateTimeField:日期时间,参数同DateField。
  • FileField:上传文件字段。
  • ImageField:继承于FileField,对上传的内容进行校验,确保是有效的图片。

选项

通过选项实现对字段的约束,选项如下:

  • null:如果为True,表示允许为空,默认值是False。
  • blank:如果为True,则该字段允许为空白,默认值是False。
  • 对比:null是数据库范畴的概念,blank是表单验证范畴的。
  • db_column:字段的名称,如果未指定,则使用属性的名称。
  • db_index:若值为True, 则在表中会为此字段创建索引,默认值是False。
  • default:默认值。
  • primary_key:若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用。
  • unique:如果为True, 这个字段在表中必须有唯一值,默认值是False。

字段查询

实现sql中where的功能,通过过滤器filter()、exclude()、get() 

语法如下:

说明:属性名称和条件运算符间使用两个下划线,所以属性名不能包括多个下划线。

过滤器(属性名__条件运算符=值)  # 这里的_是两个

条件运算符

1.exact : 判断相等

# 要求: 查询id为1的班级
c = ClassInfo.objects.filter(id__exact=1)

# 等价于
c = ClassInfo.objects.filter(id=1)

这里ClassInfo.objects.filter(id__exact=1)返回的是一个Queryset,简单理解Queryset就像Python中的list

<QuerySet [<ClassInfo: ClassInfo object (1)>]>说明里面只有一个ClassInfo对象

我们可以用c[0]查看name属性

得到的就是id=1那个属性的名字为python class

    想详细了解Queryset的话,后续会推出

2. contains: 判断是否包含

# 查询班级名是否包含'python'的班级
c = ClassInfo.objects.filter(name__contains=‘python’)

 3.startwith、endswith: 查询以指定开头或结尾

# 查询班级以class结尾的班级
c = ClassInfo.objects.filter(name__endswith='class')

 4.isnull:查询是否为null

# 查询班级名不为null的班级
c = ClassInfo.objects.filter(name__isnull=False)

5.in: 是否包含在范围内

# 查询id为1、3、17的班级
c = ClassInfo.objects.filter(id__in=[1,3,17])
# 这里会返回三个ClassInfo对象

# 上面查询的都是在数据库里有的数据,如果要是查询一个不存在的数据会怎样?
c = ClassInfo.objects.filter(id__in=[1,3,5])
# 可以通过上面的数据看出,这里1,3都是包含在内的,但是5是不包含在内的

结果可以的看出,这里返回了除了id=5的所有相等的ClassInfo对象

6.比较查询---> gt、gte、lt、lte:大于、大于等于、小于、小于等于。

# id大于3的班级
c = ClassInfo.objects.filter(id__gt=3)

7.不等可以使用exclude()过滤器

# 查询不等id为3的班级
c = ClassInfo.objects.exclude(id=3)

可以看出上面没有返回id=3的ClassInfo对象

8.日期查询---->year、month、day、week_day、hour、minute、second

# 查询2019年开班的班级
c = ClassInfo.objects.filter(class_date__year=2019)

# 查询2019年6月20日以后开班的班级
from datetime import date
c = ClassInfo.objects.filter(class_date__gt=date(2019,6,20))

两个属性相比较怎么办?那么就来介绍一个F对象,但是没什么办法好举例,所以下面加一个属性

让我们来复习一下,之后需要用到生成迁移文件,和迁移

python manage.py makemigrations

python manage.py migrate

然后修改一id为一的班级那条数据库,将reported_number 改为14

UPDATE `school_classinfo` SET 
    `name` = 'python class', 
    `class_date` = '2019-06-21',
    `class_size` = 14, 
    `reported_number` = 14, 
    `isDelete` = 0 
WHERE `school_classinfo`.`id` = 1;

数据准备完毕,我们来接着学习F对象

# 查询班级人数和报道人数相等的数据,也就是两个属性间的比较
from django.db.models import F  # 想要使用F对象,必须要导包
c = ClassInfo.objects.filter(class_size=F('reported_number'))

这里报道人数和班级人数相等的只有两个,分别是id=1、id=17的

Q对象

多个过滤器逐个调用表示逻辑与 [&] 关系,同SQL中的where里的and关键字

Q对象被义在django.db.models中。

# 查询班级人数大于10,并且报道人数大于10的
c = ClassInfo.objects.filter(class_size__gt=10,reported_number__gt=10)
或者
c = ClassInfo.objects.filter(class_size__gt=10).filter(reported_number__gt=10)
或者
from django.db.models import Q 
c = ClassInfo.objects.filter(Q(class_size__gt=10) & Q(reported_number__gt=10))

 

如果需要实现逻辑或 [ | ] 的查询,需要使用Q()对象结合 | 运算符,同SQL语句中的where里的or关键字

# 查询班级人数大于10的,或者id 小于3 的班级
c = ClassInfo.objects.filter(Q(class_size__gt=10) | Q(id__lt=3))

这里返回了两个ClassInfo对象,是因为或只要满足一个条件就成立:

  1. id=1 的是都成立,既满足班级人数大于10.,也满足id小于3
  2. id=2 的id小于3这个成立,所以也会返回在Queryset中

Q也可以使用非~操作符

# 查询id不等于3的班级
c = ClassInfo.objects.filter(~Q(id=3))

这里除了id=3的班级都会返回到Queryset中

聚合函数:使用aggregate()过滤器调用聚合函数[Avg,Count,Max,Min,Sum]使用时候需要导入django.db.models

# 查询所有学生
from django.db.models import Sum
c_dict = ClassInfo.objects.aggregate(Sum('class_size'))

使用count时一般不使用aggregate()过滤器。

# 查询班级总数
class_count = ClassInfo.objects.count()

count()返回的是一个数字

Queryset:Queryset表示从数据库中获取的对象集合,在管理器上调用某些过滤器方法会返回Queryset,Queryset可以含有0个、1、或者多个过滤器,过滤器基于所给的参数有限制查询结果,从SQL的角度,Queryset和select语句等价,过滤器类似where、limit

返回多个值的过滤器:

  • all(): 返回所有数据
  • filter(): 返回满足条件的数据
  • exclude():返回满足条件之外的数据
  • order_by(): 对结果进行排序

返回单个值得过滤器:

  • get():返回单个满足条件的对象
    • 如果没找到会报DoesNotExist异常
    • 如果多条被返回,会返回MultipleObjectsReturned异常
  • count():返回当前查询结果的总条数
  • aggregate(): 聚合,返回一个字典

判断某一个Queryset中是否有数据:

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

三大特性

  • 惰性执行:创建Queryset不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用
  • 缓存:使用同一个Queryset,第一次使用时会发生数据库的查询,然后把结果缓存下来,再次使用这个Queryset时,返回的是缓存数据
  • 限制查询:可以对Queryset进行取下标或切片操作,等同于sql中的limit和offset子句。注意:不支持负数索引。
    • 如果获取一个对象,直接使用[0],但是如果没有数据,[0]引发IndexError异常

模型类关系

关系型数据库的关系包括三种:

  • ForeignKey:一对多,将属性定义在多的中

  • ManyToManyField:多对多,将属性定义在任意类中

  • OneToOneField:一对一将属性定义在任意类中

  • 可以维护递归的关联关系,使用self指定,应用在自关联

一对多关系:之前我们一直使用的ClassInfo类和StudentInfo类就是一对多的关系

多对多的关系:创建一个课程表类与学生,每个学生有多门课程,一门课程也会有很多学生,在创建多对多关系的时候,除了学生表和课程表之外会多创建一个中间表,分别都是他们的id

# 在models.py中创建课程表类
class Timetable(models.Model):
    """课程表模型类"""
    name = models.CharField(max_length=30)  # 课程表名称
    school_time = models.TimeField()  # 课程时间
    stu_table = models.ManyToManyField(StudentInfo)  # 课程表和学生属于多对多的关系

生成迁移文件,并且迁移,

python manage.py makemigrations
python manage.py migrate

关联查询:Django也能实现类似SQL中的join查询

通过对象执行关联查询:在定义模型类的时候可以指定关联关系,最常用的就是一对多的关系

# 查询id为1的班级下的所有学生
# 1.首先来获取id为1的班级
c = ClassInfo.objects.get(id=1)
s = c.studentinfo_set.all()  # 这里需要由ClassInfo类.学生类名小写_set
# SQL语句
SELECT
* FROM `school_studentinfo` WHERE `school_studentinfo`.`the_class_id` = 1 LIMIT 21;

通过SQL语句来来对比一下在Python manage.py shell 中的数据:

# 查询id=18的学生所在的班级
s = StudentInfo.objects.get(id=18)
s.the_class
# SQL语句
SELECT * FROM `school_classinfo` WHERE `school_classinfo`.`id` = 1;

通过多模型类访问关联一模型类的语法

# 多对应的模型类对象.关联类属性_id
s = StudentInfo.objects.get(id=18)
s.the_class_id
对比
s.the_class

让我们测试一下所学的关联查询

# 查询id为1的班级
c = ClassInfo.objects.get(id=1)  # SELECT * FROM `school_classinfo` WHERE `school_classinfo`.`id` = 1;

# 获取C班级的所有学生
c.studentinfo_set.all()  # SELECT * FROM `school_studentinfo` WHERE `school_studentinfo`.`the_class_id` = 1  LIMIT 21;

# 获取id = 18 的学生 
s = StudentInfo.objects.get(id=18)  # SELECT * FROM `school_studentinfo` WHERE `school_studentinfo`.`id` = 18;

# s这个学生所在的班级
s.the_class  # SELECT * FROM `school_classinfo` WHERE `school_classinfo`.`id` = 1;

通过模型类执行关联查询

 由多模型类条件查询一模型类数据语法:

关联模型类名小写__属性名__条件运算符 = 值
# 那么让我们做一个例子
# 查询班级,要求班级中的学生爱好里包含‘肉’的,返回所在的班级
class_list = ClassInfo.objects.filter(studentinfo__interest__contains = '肉')


# 多模型类查询,但是由一模型类条件查询
# 查询班级名为python class班下的所有学生
stu_list = StudentInfo.objects.filter(the_class__name = 'python class')

自关联:地区信息、分类信息、等等数据,表结构基本固定的,每个表的数据量不是特别庞大的,为了充分利用数据表的搭理那个数据存储功能,可以设计成一张表,内部的关系字段指向本表的主键,这就是自关联的表结构

# 第一步咱们开始准备模型类 models.py 下创建AreaInfo模型类
class
AreaInfo(models.Model): """地区模型类""" name = models.CharField(max_length=50) # 地区名称 area_parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True) # 关系键

【扩展】:在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在  django.db.models中包含了可选常量:

关联属性on_delete选项的取值

  • models.CASCADE 此为默认值,级联删除,会删除关联数据

    models.ForeignKey('self', on_delete=models.CASCADE)

  • models.PROTECT 只要存在关联数据就不能删除

    models.ForeignKey('self', on_delete=models.PROTECT)

  • models.SET_NULL 删除数据后关联字段设置为NULL,仅在该字段允许为null时可用(null=True

   models.ForeignKey('self', on_delete=models.PROTECT,null=True)

生成迁移文件,并且迁移

python manage.py makemigrations  # 生成迁移文件
python manage.py migrate  # 迁移

接下来就是将省、市、区县的数据导入

我在windows的环境下,所以编码问题很是头疼这里是通过Navicat导入的数据:

数据在这里:

链接:https://pan.baidu.com/s/1SIPtBmQRoEnL_DYDYIB9qQ 
提取码:r3px 

 M准备完成,开始准备V,定义视图views.py下的area

# 这里需要导入AreaInfo这个类
from school.models import AreaInfo
# 创建地区信息的视图
def area(request):
    # 获取数据,由于数据太多咱们选择一个省的数据 230100=哈尔滨
    area = AreaInfo.objects.get(id=230100)
    return render(request, 'school/area.html', {'area': area})

设置URLconf配置

1 from django.conf.urls import url
2 from school import views
3 urlpatterns = [
4     url(r'index$', views.index),
5     url(r'create$', views.create),
6     url(r'delete(d+)$', views.delete),
7     url(r'area$', views.area),  # 新增的url
8 ]

Django框架MVT,MV已经准备完,现在来创建模板(T)area.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 style="text-align: center">Circle & 地区</h1>
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10>
    <div style="padding-left: 200px">当前市: {{ area.name }}</div>
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10>
    <div style="padding-left: 200px">{{ area.name }}的省是: {{ area.area_parent.name }}</div>
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10>
    <div style="padding-left: 200px">
        下级区 县 市:
        <ul>
            {% for a in area.areainfo_set.all %}
                <li>
                    {{ a.name }}
                </li>
            {% endfor %}

        </ul>
    </div>
</body>
</html>

返回的结果是

模型类实例方法:

  • str(): 将对象转换成字符串
  • save(): 将对象保存到数据表中
  • delete(): 将对象从数据表中删除

模型类的属性

  objects

  模型当中最重要的属性是 Manager。它是Django模型和数据库查询操作之间的接口,并且它被用作从数据库         当中查询,如果没有指定自定义的 Manager 默认名称是 objects。Manager只能通过模型类来访问,不能通           过模型实例来访问。

管理器(Manager):是向Django模型提供数据库查询操作的接口。Django应用程序中的每个模型至少有一个管理器。

管理器命名(Manager):默认Django会为每一个模型类添加一个命名为objects的管理器 ,如果你想用objects作为字段名,或者你想用除了objects以外的名字为这个管理器命名,你可以根据模型类来重命名,将重命名的管理器给定一个类,在这个模型类上定义一个models.Manager()类型的类属性。例如:

from django.db import models


class ClassInfo(models.Model):
    class_manager = models.Manager()

使用上面的例子,ClassInfo.objects将会产生一个AttributeError的异常,但是ClassInfo.class_manager.all() 将会提供ClassInfo对象的列表

自定义管理器:你可以用一个自定义管理器在模型中扩展这个基本的管理器,和在模型中实例化自定义管理器。

  自定义管理器有两个好处,也许正是你想要修改管理器的两个原因

    1.修改管理器返回的初始Queryset。

    2.添加额外的管理器方法

之前我们使用的all()函数都是直接返回Queryset,这里我来学习一下修改管理器返回的初始Queryset

# 首先我们来定义一个管理器
class ClassInfoManager(models.Manager):
    """班级管理器"""
    # 重写all()方法
    def all(self):
        # 调用的父类的all()方法,默认获取所有的班级信息
        class_model = super().all()
        # 修改查询的结果,将isDelete 为 True 的不返回
        # 返回结果
        return class_model.filter(isDelete=False)


class ClassInfo(models.Model):
    """班级模型类"""
    name = models.CharField(max_length=20)  # 班级名
    class_date = models.DateField()  # 开班日期
    class_size = models.IntegerField(default=0)  # 班级人数 默认0
    reported_number = models.IntegerField(default=0)  # 报到人数
    isDelete = models.BooleanField(default=False)  # 逻辑删除 默认False
    objects = ClassInfoManager()  # 将自定义的管理器赋值给objects管理器对象

第二个好处就是添加额外的管理器方法,还记得之前在网站点击创建就会添加一个班级,这里我们可以将它把创建方法封装到models的管理器中

管理器:models.py

# 首先我们来定义一个管理器
class ClassInfoManager(models.Manager):
    """班级管理器"""
    # 重写all()方法
    def all(self):
        # 调用的父类的all()方法,默认获取所有的班级信息
        class_model = super().all()
        # 修改查询的结果,将isDelete 为 True 的不返回
        # 返回结果
        return class_model.filter(isDelete=False)

    # 创建一个新的方法:添加班级
    def create_class(self, name, class_date, class_size=0, reported=0):
        # 创建对象
        c = self.model()  # 等价于 c = ClassInfo()
        # print(type(c))  # 测试self.model()的返回值类型
        c.name = name
        c.class_date = class_date
        c.class_size = class_size
        c.reported_number = reported
        c.isDelete = False
        c.save()
        return c

视图:views.py

# 创建新的班级类
def create(request):
    """
    c = ClassInfo()
    c.name = "海贼团 class"
    c.class_date = date(2019, 6, 21)
    c.class_size = 0
    c.isDelete = 0
    c.save()
    return redirect('/index')
    """
    ClassInfo.objects.create_class("web class", '2019-06-23')
    return redirect('/index')

元选项(Meta options) : 设置元信息,需要在模型类中定义一个Meta的内部类

这货到底有啥用,简单的说明一下。当我们定义完模型类的时候,生成的数据库表名都是 应用名_模型类名(例如:school_classinfo),但是我还不想让他在数据库生成这种名字,我们就可以用Meta当中的一个选项,就是db_table,设置数据库的表名,具体如何操作呢?我们就动手写一下

# 这里我们就用课程表模型类演示
class Timetable(models.Model):
    """课程表模型类"""
    name = models.CharField(max_length=30)  # 课程表名称
    school_time = models.TimeField()  # 课程时间
    stu_table = models.ManyToManyField(StudentInfo)  # 课程表和学生属于多对多的关系

    class Meta:
        db_table = 'timetable'  # 设置表名为timetable

 其实仔细想想,这也是与应用名之间的解耦,还有很多的元选项之后有时间,可以做一个专辑

-To Be Continued-

- The end -

原文地址:https://www.cnblogs.com/Hannibal-2018/p/11055936.html