Django模型(MTV_Model)
什么是模型
对数据库的具象(不同业务选择不同的数据库)
Django为数据库提供了统一的调用API,不管底层操作哪种数据库,只需要模型对象操作即可,屏蔽掉了数据库操作的差异性。
配置数据
1、安装驱动
pip install pymysql
或者 mysqlclient
2、注册pymysql,工程目录下__init__.py文件中
import pymysql
pymysql.install_as_MySQLdb()
3、工程目录下settings.py文件中,修改DATABASES
# Django默认使用SQLine数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# 将项目数据库改为MySql
DATABASES = {
'default': {
# 修改为mysql(驱动)
'ENGINE': 'django.db.backends.mysql',
# 链接数据库,并创建对应的数据库HelloDjango
'NAME': 'HelloDjango',
# 用户名
'USER': 'root',
# 密码
'PASSWORD': '123456',
# 主机
'HOST': '127.0.0.1',
# 端口号
'POST': '3306'
}
}
模型开发
1、配置数据库
2、定义模型类(一个模型相当于一个数据库表)
class Student(models.Model):
name = models.CharField(max_length=40)
sex = models.CharField(max_length=10)
age = models.IntegerField()
isdelete = models.BooleanField(default=False)
# 指定元类,可以修改属性
3、生成迁移文件
python manage.py makemigrations
python manage.py makemigrations appname # 删除迁移目录时,可以使用
# 在migrations目录中生成一个迁移文件,此时数据库中还没生成表单
4、执行迁移文件,生成对应表单
python manage.py migrate
备注: 相当于执行了MySQL语句创建了表单(appname_students)
5、使用模型进行增删改查
对象.save() # 写入数据库
注:
O: 对象 | 根据对象类型生成表结构;
R: 关系 | 将对象、列表的操作转换sql语句;
M: 映射 | 将sql语句查询到的结果转为对象、列表;
模型属性
一、概念陈述
- Django根据属性的类型确定以下信息:
当前选择的数据库支持字段的类型;
渲染管理表单时使用的默认html控件;
在管理站点最低限度的验证;
- Django的主键
会为表增加自增长的主键列,每个模型只能有一个主键列;
如果使用选项设置某属性为主键列后,则Django不会再生成默认的主键列;
- 属性命名规则:
遵循标识符规则;
由于Django的查询方式,不允许使用连续的下划线;
二、 库
- 定义属性时
需要字段类型,字段类型是被定义在django.db.models.fields目录下;
为了方便使用,被导入到django.db.models中;
- 使用方式
导入from django.db import models;
通过models.field创建字段类型的对象,赋值给属性;
三、删除逻辑
对于重要数据都做逻辑删除,不做物理删除;
实现方法是定义isDelete属性,类型为BooleanField,默认值为False;
四、字段类型
- AutoField
一个根据实际ID自动增长的IntegerField,通常不指定;
如果不指定,一个主键字段将自动添加到模型中;
- 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;
auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果;
- TimeField
使用Python的datetime.time实例表示的时间,参数同DateField;
- DateTimeField
使用Python的datetime.datetime实例表示的日期和时间,参数同DateField;
- FileField
一个上传文件的字段;
- ImageField
继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image;
五、字段约束
- null
如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False;
- blank
如果为True,则该字段允许为空白,默认值是 False;
null是数据库范畴的概念,blank是表单验证证范畴的;
- db_column
字段的名称,如果未指定,则使用属性的名称;
- db_index
若值为 True, 则在表中会为此字段创建索引;
- verbose_name
Admin(后台显示的名称)中字段的显示名称;
- default
默认值;
- primary_key
若为 True, 则该字段会成为模型的主键字段;
- unique
如果为 True, 这个字段在表中必须有唯一值;
六、表关系
- ForeignKey:一对多,将字段定义在多的端中
格式: 对象.模型类小写_set
示例: grade.students_set
- OneToOneField:一对一,将字段定义在任意一端中
格式: 对象.模型类小写
示例: grade.students
- 访问id
格式: 对象.属性_id
示例: student.sgrade_id
- ManyToManyField:多对多,将字段定义在两端中
外键: 属于数据库约束,外键默认是可以重复的,对应另外一张表的主键(一对多)
模型查询
名词:查询集、过滤器相当于select和where
查询集:
- 在管理器上调用过滤器方法返回查询集;
- 查询集经过过滤器筛选后返回新的查询集,所以可以写出链式调用;
- 惰性执行(创建查询集不会带任何数据访问,直到调用数据时才会访问数据)
直接访问:
- 迭代
- 序列化
- 与if合用
过滤器:
- all() 返回查询集中所有数据(一条数据即一个对象)
students = Student.objects.all()
- filter() 返回符合要求的数据
filter(键=值)
filter(键=值,键=值)
filter(键=值).filter(键=值)
students = Student.objects.filter(s_score=91)
students = Student.objects.filter(s_score__lt=60)
- exclude() 过滤掉符合条件的数据
students = Student.objects.exclude(s_score=91)
- order_by() 排序
students = Student.objects.order_by('s_score') # 升序
students = Student.objects.order_by('-s_score') # 降序
- values() 返回查询集中所有数据(一条数据即一个字典),返回一个列表
# lt小于、lte小于等于、gt大于、gte大于等于、ne不等于
返回单个数据:
- get() 返回一个满足条件的对象
如果没有找到符合条件的对象,会引发'DoesNotExist'异常
如果找到多个对象,会引发'MultipleObjectsReturned'异常
- count() 返回查询集的对象个数
students = Student.objects.filter(pk=1)
if students.count()>0: # 有数据
pass
else:
pass
- first() 返回查询集中的第一个对象
student = students.first()
- last() 返回查询集中的最后一个对象
students = students.last()
- exists() 判断查询集中是否有数据,有数据返回True
if students.exists(): # 有数据
pass
else:
pass
# 集合 = 类名.objects.get(pk=91), pk是primary key缩写!
限制查询集(切片)
查询集返回列表,可以使用下标的方法进行限制,等同于sql语句中的limit语句;
例如: studentList = Students.objects.all()[0:5]
注意: 下标不能是负数
查询集缓存:
- 每个查询集都包含一个缓存,来最小化的数据访问
- 在新建的查询集时,缓存首次为空,第一次对查询集求值,会发生数据缓存
- django会将查询出来的数据做一个缓存,并返回查询结果,以后查询直接使用查询集的缓存
元类:
- 在模型类中定义Mata类,用于设置元信息
- db_table属性
定义数据表名,推荐使用小写字母,数据表名默认为项目名小写_类名;
- ordering属性
对象的默认排序字段,获取对象的列表时使用;
ordering = ['id'] 升序
ordering = ['-id'] 降序
ordering = ['sage'] 按sage升序
注意: 排序会增加数据库的开销;
举例:
__str__内置方法,一般说明类的说明,或者自己定义输出。
# 学生表students
class Students(models.Model):
# 在没有添加主键时,它会在生成时自动添加主键
# id = models.IntegerField()
sname = models.CharField(max_length=30)
ssex = models.CharField(max_length=10)
sage = models.IntegerField()
sbrief = models.CharField(max_length=50)
isdel = models.BooleanField(default=False)
lastTime = models.DateTimeField(auto_now=True)
createTime = models.DateTimeField(auto_now=True)
# sclass = models.CharField(max_length=10)
# 关联外键(学生都会有一个班级)
sclass = models.ForeignKey('Grades')
def __str__(self):
return '%s %s' % (self.sname,self.sclass)
# 元选项(元信息)
class Meta:
db_table= 'students'
# ordering = ['id']
ordering = ['sage']
字段查询
- 实现了sql中的where语句,作为方法filter()、excluede()、get()的参数
- 语法
属性名称__比较运算符=值
- 外键
属性名_id
- 转义
like语句中使用%为了匹配占位
filter(sname__contains='%')
# 比较运算符
- exact 判断(大小写敏感)
filter(isdel=False)
- contains 是否包含(大小写敏感)
studentList = Student.objects.filter(sname__contains='孙')
- startswith、endswith 以value开头或结尾(大小写敏感)
studentList = Student.objects.filter(sname__startswith='孙')
备注:
以上四个前面加上i,表示不区分大小写;
iexact、icontains、istartswith、iendswith
- isnull、isnotnull 是否为空
filter(sname__isnull=False)
- in 是否包含在范围内
studentList = Students.objects.filter(pk__in=[2,3,4,5,6])
- gt大于、gte大于等于、lt小于、lte小于等于
studentList = Students.objects.filter(sage__gt=30)
- year、mouth、day、week_day、hour、minute、second 时间相关
studentList = Student.objects.filter(lastTime__year=2018)
- 跨关联查询 (处理join查询)
语法: 模型类名__属性名__比较符
例如: 学生描述中带有'张三'的学生属于哪个班级
grade = Grades.objects.filter(students__sbrief__contains='张三')
- 查询快捷
pk 代表主键(因为主键并不一定就是id字段)
student = Students.objects.get(pk=3)
#聚合函数
使用aggregate()函数返回聚合函数的值【sql中的函数使用】
例如: Avg()、Count()、Max()、Min()、Sum()
实例:
from django.db.models import Max
maxAge = STudents.objects.aggregate(Max('sage'))
# F对象
- 可以使用模型的A属性与B属性进行比较.
例如: Grades.objects.filter(ggirlnum__gt=F('gboynum'))
- 支持F对象的算术运算
例如: Grades.objects.filter(ggirlnum__gt=F('gboynum')+20)
# Q对象
- 概述
查询条件的封装
- 需求
进行or查询、进行and查询、进行~查询 【与或非操作】
- 解决
使用Q对象
- 例如
studentList = Students.objects.filter(Q(pk__lte=3) | Q(sage__gt=50))
studentList = Students.objects.filter(Q(pk__lte=3) # 只有一个Q对象就用于匹配
studentList = Students.objects.filter(~Q(pk__lte=3) # 取反
模型成员
# 类属性
- objects管理器
是Manager类型的一个对象,作用是与数据库交互;
当定义模型类是没有指定管理器,则Django为模型创建一个名为objects的管理器;
- 自定义管理器
当为模型指定模型管理器,Django就不在为模型类生成objects模型管理对象;
studentsObj = models.Manager() # 自定义模型管理器
- 模型管理器作用
模型管理器是Django的模型与数据库进行交互的接口;
一个模型可以有多个模型管理器;
- 自定义管理器Manager类
向着管理器中添加额外的方法;
修改管理器返回的原始查询集(重写get_queryset());
# 自定义管理器
Students.objects.all() # 默认时
Students.studentObj.all() # 自定义管理器为studentObj
, , , , , , , , , , , , , , , , , , , , '...(remaining elements truncated)...']>
# 自定义管理器Manager类
class StudentsManager(models.Manager):
# 修改管理器返回的原始查询集合
def all(self):
# 调用父类方法,在父类方法基础上进行再次过滤
return super().all().filter(isdel=False)
# 学生表students
class Students(models.Model):
# 自定义模型管理器
studentObj1 = models.Manager()
# 用到自定义管理器(对数据进行过滤)
studentObj2 = StudentsManager()
# 使用1: Students.studentObj1.all()
# 使用2: Students.studentObj2.all()
# 创建模型对象
- 目的
向数据库中添加数据
- 注意
当创建对象时,django是不会对数据库进行读写操作;
当调用save()方法时才会与数据库交互,将对象保存到数据库表单中;
__init__方法已经在父类models.Model中使用,在自定义的模型中无法使用;
- 在模型类中添加一个类方法(为了创建对象)
# 学生表students [models.py文件中]
class Students(models.Model):
...
# 定义一个类方法创建对象
@classmethod # 这表明就是类方法
def createStudents(cls,name,sex,age,brief,grade,lastT,createT,isd=False): # cls就表示Students这个类
student = cls(sname=name,ssex=sex,sage=age,sbrief=brief,sclass=grade,lastTime=lastT,createTime=createT,isdel=isd)
return student
# 调用即快速创建学生对象 [views.py文件中]
stu = Students.createStudents('测试数据','男',18,'我是测试数据,别太在意.',grade,'2018-05-30','2018-05-29')
- 在定义管理器中添加一个方法(为了创建对象)
# 自定义管理器Manager类
class StudentsManager(models.Manager):
def all(self):
# 调用父类方法,在父类方法基础上进行再次过滤
return super().all().filter(isdel=False)
def createStudent(self,name,sex,age,brief,grade,lastT,createT,isd=False):
# self.model() 其实就是创建Students对象
stu = self.model()
stu.sname = name
stu.ssex = sex
stu.sage = age
stu.sbrief = brief
stu.sclass = grade
stu.lastTime = lastT
stu.createTime = createT
return stu
# 调用即快速创建学生对象 [views.py文件中]
stu = Students.studentObj2.createStudent('测试数据','男',18,'我是测试数据,别太在意.',grade,'2018-05-30','2018-05-29')
模型的增删改查
# 查询数据
- 根据不同条件,获取对应的数据
# 添加数据
- 实例化模型对象
- 设置对象属性
- 调用对象的save()方法
# 更新数据
- 查询获取到需要更新的对象
- 设置对象数据
- 调用对象的save()方法
# 删除数据
- 查询获取需要删除出的对象
- 调用对象的delete()方法
主表(重要数据),从表(声明关系)
模型关系-一对一
models.OneToOneField()进行关联.绑定一对一关系,默认情况下,一方被删除,被绑定的也会被删除。
# 定义模型类
# 人
class Person(models.Model):
p_name = models.CharField(max_length=30)
p_age = models.IntegerField(default=1)
# 身份证
class IDCard(models.Model):
i_num = models.CharField(max_length=36)
i_sex = models.BooleanField(default=False)
# 一对一 [oneToOne,对应的是唯一的]
i_person = models.OneToOneField(Person, default=1)
备注:
(默认情况)删除人,关联的身份证会被自动删除;
删除身份证,人不受影响的;
身份证关联到人,身份证是主动方,即卡跟随于人的;
主表(Person),从表(IDCard) [声明关系的表都是属于从表]
# 获取数据
- 从获取主,关系字段是直接声明的,它是一个显性属性
id_cart = IDCart.objects.last()
person = id_cart.i_person # 身份证对应的人
- 主获取从,关系字段是隐性属性,访问方式(小写): 对象.关联模型
person = Person.objects.last()
idcart = person.idcart # 人对应的身份证,关系模型名IDCard,但要写成小写!!!
模型关系-一对多
# 定义模型类
# 人
class Person(models.Model):
p_name = models.CharField(max_length=30)
p_age = models.IntegerField(default=1)
# 爱好
class Hobby(models.Model):
h_name = models.CharField(max_length=30)
h_cost = models.FloatField(default=1000)
# 一对多
h_person = models.ForeignKey(Person, on_delete=models.PROTECT)
备注:
默认情况,删除人的时,对应的爱好都会被删除;
可以设置保护模式,或者设置默认值模式;
# 获取数据-级联
- 从获取主,显性属性,直接获取
hobby = Hobby.objects.last()
person = hobby.h_person # 爱好对应的人
- 主获取从,隐性属性,访问方式: 对象.关联模型_set [与objects同源,所以用法也是一致]
person = Person.objects.last()
# hobby_set与objects同源,all表示所有数据
hobbies = person.hobby_set.all() # 人对应的爱好
# 按条件获取
hobbies = person.hobby_set.filter(id=2)
模型关系-多对多
数据库中是不存在多对多的,多对多是一个抽象出来的。底层通过两个外键实现,两个外键存在关系表中。
# 定义模型类
# 用户
class User(models.Model):
# 名字
u_name = models.CharField(max_length=100)
# 等级
u_level = models.IntegerField(default=1)
# 商品
class Goods(models.Model):
# 名字
g_name = models.CharField(max_length=100)
# 价格
g_price = models.FloatField(default=1)
# 声明关系
g_user = models.ManyToManyField(User)
备注:
删除数据时,会删除自己表对应的数据和关系中的数据;
# 级联数据的获取
- 从获取主,显性属性,直接获取 [与objects同源,所以用法也是一致]
goods = Goods.objects.last()
# g_user是显性属
user = goods.g_user.all()
- 主获取从,隐性属性,访问方式: 对象.关联模型_set [与objects同源,所以用法也是一致]
user = User.objects.last()
goodslist = user.goods_set.all()
删除模式
models.CASECADE默认模式, 默认删除级联数据
models.PROTECT保护模式,当存在级联数据的时候,删除会抛出保护异常; 不存在级联数据时,是允许删除的;
- models.SET_NULL 置空模式【字段本身允许为空】
# 需要再加上允许为空
# 可以删除人,但卡对应的人就会置为null
i_person = models.OneToOneField(Person, on_delete=models.SET_NULL, null=True)
- models.SET_DEFAULT 置默认值 【字段本身有默认值】
i_person = models.OneToOneField(Person, on_delete=models.SET_DEFAULT, default=1)
- models.SET() 删除时候重新动态指向一个实体访问对应元素 【具体值】
模型继承
- 默认模型就是运行继承的(默认的继承处理不是很合理)
默认父类中定义字段还是在父类表单中;
子类通用数据部分会在父类表单中,子类特有数据在字表中,子类通过外键进行级联;
【默认模式有级联,效率会比较低】
- 开发中需要将父类抽象化
即在父类模型中的元信息中使用abstract = True;
抽象化的父类,不会在数据库中生成表单;
子类会将父类中通用数据,复制到子表中;
class Animal(models.Model):
a_name = models.CharField(max_length=20)
a_color = models.CharField(max_length=20)
class Meta: # 抽象化
abstract = True
class Dog(Animal):
d_bark = models.CharField(max_length=20)
class Cat(Animal):
c_eat = models.CharField(max_length=20)