Django的Model类
1、model
用来和数据交互的(读取和传入数据)
2、orm
Object Relational Mapping对象关系映射,python中的model对象和数据库中的表做映射
3、重要概念
3.1、类-表对应
model中的一个类对应数据库中的一个表。实现方式:继承Model类(django.db.models.Model)
3.2、属性-字段
类中的属性对应数据库中的字段
3.2.1、类型
CharField 字符类型
IntegerField 数值类型,choise枚举类型数据,元祖包元祖
type = models.IntegerField(choices=((1,'机构'),(2,'课程'),(3,'讲师')),verbose_name="类别")
BooleanField 布尔类型
NullBooleanField 可以为空的布尔值
AutoField int自增列,必须填入参数 primary_key=True,auto_create,自动创建,mysql的auto_increment
FloatField 浮点型
DecimalField 同Python的Decimal类型,参数:max_digits 最大总位数,decimal_places 小数点后的位数
TextField 文本类型
UUIDField 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
# 模型类 class Order(models.Model): no = models.UUIDField(verbose_name='订单编号') price = models.DecimalField(verbose_name='订单金额',max_digits=6, decimal_places=2) pay_state = models.BooleanField(default=False) # 创建对象 import uuid Order.objects.create(no=uuid.uuid4(),price=1819.567,pay_state=True) # price只保留两位小数,会自动四舍五入
FileField 文件,字符串,路径会保存到数据库,文件上传到指定目录
参数:upload_to = "" 上传文件的保存路径,storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField 图片,路径保存到数据库,文件上传到指定目录
参数:upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateField 日期类型,格式:格式:YYYY-MM-DD
参数:auto_now 每次保存时,自动设置当前字段为当前时间,用于最后一次修改
auto_now_add 每一次保存时,自动设置为当前时间,用于“创建时间”
注意:auto_now和auto_now_add及default只能设置一个,不能组合
DateTimeField 日期时间类型 datetime.datetime,日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
TimeField 时间类型, 格式: HH:MM[:ss[.uuuuuu]]
3.2.2、约束
max_length 最大长度
default 默认值
unique 唯一值
primary_key 主键
null
blank 是否允许输入为空,即输入空字符串
db_index 索引
db_column 指定字段名
verbose_name 在admin显示字段名称
choices 一个包含多元素元组的可迭代的对象,用于给字段提供选项
3.2.3、关系
ForeignKey 一对多,将字段定义在多端,自动在多端中添加一个外键,如:store = ForeginKey(Store, db_column="外键的列名"),外键字段: store_id
OneToOneField 一对一,将字段定义在任意一端
ManyToManyField 多对多,将字段定义在两端
4、元信息
在Model的子类中定义Meta来声明表的相关信息
# 需要定义在类的内部 class Meta: app_label='' # 应用名 db_table='' # 表名 ordering = [] # 排序字段,字段名前可使用'-'表示倒序 verbose_name = '' # admin中所显示的名称 unique_together= '' # 来设置的不重复的字段组合
5、CRUD
5.1、增
5.1.1、通过模型类对象管理器插入记录
语法:模型类名.objects.create(类属性=值) 此方式是直接操作数据库
stu1 = Student.objects.create(name='Negan',sex='m',score=95.0)
5.1.2、实例化模型对象插入数据
car = Car(name="宝马3",price=20.5) car.save()
car = Car() car.name="宝马5i" car.price=50.5 car.save()
通过对象模型进行插入操作是在内存中执行的,也就是说在没有save()之前都是在内存中操作,和数据库没有关系,只有在save()后,才将数据加载到数据库
5.2、查
5.2.1过滤器
filter 选出符合条件的数据,可以包含多个字段,用“,”隔开表示与的关系
s1 = Student.objects.filter(sex='m',name='Neagn') # 支持链式调用 s1 = Student.objects.filter(sex='m').filter(name='Negan')
exclude 去掉符合条件的
s1 = Student.objects.filter(sex='m').exclude(socre=72) # 找出性别为m以及分数不是72的
5.2.2条件
语法:属性名__运算符=临界值
gt 大于
Car.objects.filter(price__gt=30)
lt 小于
gte 大于等于
lte 小于等于
exact 精确内容查找,区分大小写
iexact i 代表ignore(忽略),即忽略大小写
contains 内容包含,相当于模糊查询
Car.objects.filter(name__contains='奔')
startswith 以...开头
endswith 以..结尾
istartswith 以...开头,忽略大小写
endswith 以..结尾,忽略大小写
icontains 包含xx字符,忽略大小写
in 包含值项
Car.objects.filter(price__in=(30.58,26.65))
isnull 为null
isnotnull 非null
5.2.3、获取对象
①集合对象QuerySet(可以使用循环,也可以进行切片操作)
filter() 返回符合条件的数据
exclude() 过滤掉符合条件的数据
all() 获取所有的对象
②单个对象
get() 要求条件对应的数据有且只有一个
注意:如果没有找到符合条件的对象,会引发 模型类.DoesNotExist异常
如果找到多个,会引发 模型类.MultiObjectsReturned 异常
last() 返回查询集中的最后一个对象
first() 返回查询集中第一个对象
③其他方法
count() 返回查询集中对象的个数
exists() 判断查询集中是否有数据,如果有返回True,反之返回False
values() 一条数据就是一个字典,返回一个列表
使用values()可以查询某个(些)字段
模型类名.objects.values("属性名")或模型类名.objects.values("属性名1","属性名2",...),返回QuerySet容器对象,其中的元素以字典格式表示
模型类名.objects.values_list("属性名")或模型类名.objects.values_list("属性名1","属性名2",...),返回QuerySet容器对象,其中的元素以元祖格式表示
order_by() 排序
5.2.3、聚合函数aggregate()
帮助我们去计算一些属性的值
需要先导入相关的包
from django.db.models import Count,Min,Sum,Max,Avg
avg_score = Student.objects.all().aggregate(Avg("score")) # 返回一个字典 {'score__avg': 69.42857142857143} # 自定义结果字典的key avg_score =Student.objects.all().aggregate(avgscore=Avg("score")) >> {'avgscore': 69.42857142857143}
# 也可以写成下面的形式,默认计算的是所有的结果集
avg_score =Student.objects.aggregate(avgscore=Avg("score"))
5.2.4、分组查询annotate()
students = Student.objects.values('sex').annotate(Avg('score'))
5.2.5、F对象
可以将自己的属性作为条件值
from django.db.models import F Store.objects.filter(id=F('years')) #查询Store的id和years两个属性相同的记录 Company.objects.filter(boy__lt=F("girl")) # 查询男生比女生人数少的公司
并且支持算术运算
grades = Grade.objects.filter(ggirlnum__gt=F('gboynum') +10 ) Store.objects.filter(id=1).update(years=F('years')+5)
5.2.6、Q对象
查询条件的封装,过滤器方法中的关键参数,常用语组合条件
from django.db.models import Q Student.objects.filter(Q(sage__lt=25)) # 查询年龄小于25
可以进行逻辑运算:
& 与
| 或
~ 非
Store.objects.filter(Q(years=1) | Q(years=2)) Store.objects.filter(~Q(years=1))) Store.objects.filter(Q(years__gt=1) & Q(years__lt=3))
5.3、原生sql查询
5.3.1、extra()方法
queryset.extra(参数)
students = Student.objects.filter(name__contains='郭').extra(where=['score>55','age>50'])
5.3.2、raw()方法
对象管理器.raw(原生sql) 注意:查询字段必须包含主键
books = Book.objects.raw("select id,name,price from books")
5.3.3、execute()
类似于pymysql的用法,灵活度很高,并且完全不依赖Model类
from django.db import connection cursor = connection.cursor() cursor.execute("select name,price from books") books = cursor.fetchall() # 包含结果集的元组
5.4、删
delete() 基于查询, 先查取出来,再执行删除
s = Student.objects.get(id=3)
s.delete()
5.5、改
save() 基于查询,先查询出来,再去修改,存储
也可以使用 filter(xxx=). update(xxx=)
6、模型对应关系
6.1、显性对象
开发者手动创建的对象,在模型类中,开发者声明的属性字段
6.2、隐性对象
系统为我们自动补充创建的对象
objects是一个Manager类型的对象,作用域数据库进行交,当模型类没有指定管理器的时候,Django会自动为我们创建模型管理器,如果手动指定,则系统不会在为我们生成这个对象。
如下创建一个model类,迁移后可以正常查询。
class Animal(models.Model):
name = models.CharField(max_length=10)
is_delete = models.BooleanField(default=False) # 逻辑删除
class Meta:
db_table = 'animal'
但是如果手动指定一个管理器,则会抛出异常。
class Animal(models.Model):
name = models.CharField(max_length=10)
is_delete = models.BooleanField(default=False)
m = models.Manager() # 指定管理器
class Meta:
db_table = 'animal'
>>> from myapp.models import * >>> a = Animal.objects.all() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: type object 'Animal' has no attribute 'objects'
这时候需要用我们自己定义的管理器进行查询,也就是我们定义的m
>>> a = Animal.m.all() >>> a <QuerySet [<Animal: Animal object (1)>, <Animal: Animal object (2)>, <Animal: Animal object (3)>, <Animal: Animal object (4)> ]>
我们需要取is_delete=False的数据,一般情况下都是filter(is_delete=False)这种写法,但是这种写法重复率太高,那么能不能封装一个方法?
只需要写一个我们自己的管理器类,继承models.Manager,重写get_queryset()方法。
class AnimalManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(is_delete=False) class Animal(models.Model): name = models.CharField(max_length=10) is_delete = models.BooleanField(default=False) objects = AnimalManager()
class Meta: db_table = 'animal'
>>> a = Animal.objects.all() >>> a <QuerySet [<Animal: Animal object (1)>, <Animal: Animal object (4)>]>
6.3、一对一
6.3.1、实现方式
在负责维护关系的“一”方添加OneToOneField型的类属性
主表:重要的表
从表:维护关系的表
from django.db import models class Person(models.Model): name = models.CharField(max_length=20) age = models.IntegerField() sex = models.CharField(max_length=10) class Card(models.Model): cardno = models.CharField(max_length=20,unique=True) # 卡号类属性 color = models.CharField(max_length=10) person = models.OneToOneField(Person,on_delete=models.CASCADE) # 使用OneToOneField进行“一对一”关联
6.3.2、对象间的关联
方式一:通过主动一方模型类属性,关联另一方对象
#创建人对象: per = Person(name='张三',age=20,sex='男') per.save() #通过Card类的person属性关联 card = Card(cardno='zs123456',color='绿卡',person=per) card.save()
方式二:通过对应表的唯一外键字段关联
per1 = Person(name='李四',age=22,sex='男') per1.save() card1 = Card.objects.create(cardno='ls123456',color='黄色',person_id=per1.id)
6.3.3、查询
从维护关系的"一"方查询:使用模型类中维护关系的那个类属性
#查询3号卡关联的人 card = Card.objects.get(id=3) per = card.person # 使用关联的类属性查询
从不维护关系的"一"方查询:使用对方模型类名的小写
查询1号人的卡: per1 = Person.objects.get(id=1) card1 = per1.card # 使用对方模型类名的小写
6.4、一对多
6.4.1、实现方式
通过在"多"方模型类中添加外键属性ForeignKey
class School(models.Model): # "一"方模型 name = models.CharField(max_length=30) address = models.CharField(max_length=30) history = models.TextField(null=True) class Student(models.Model): # "多"方模型 name = models.CharField(max_length=10) age = models.IntegerField() score = models.FloatField() sex = models.CharField(max_length=10) school = models.ForeignKey(School,on_delete=models.CASCADE)
6.4.2、对象间的关联
方式一:通过"多"方模型的外键类属性关联"一"方模型的实例化对象
#添加学校对象(“一”方模型对象) school1 = School.objects.create(name='清华大学',address='北京中关村') #school2 = School.objects.create(name='北京大学',address='北京',history='北京大学是一所知名大学') school3 = School.objects.create(name='西安交通大学',address='西安',history='交大很好') #通过外键类属性关联: stu1 = Student.objects.create(name='张三',age=20,score=93.5,sex='男',school=school1)
方式二:通过“多”方对应表的外键关联"一"方
通过“多”方对应表的外键关联 eg:两个学生都上的是3号学校 stu3 = Student.objects.create(name='郭靖',age=23,score=80,sex='男',school_id=3) stu4 = Student.objects.create(name='黄蓉',age=21,score=85,sex='女',school_id=3)
6.4.3、查询
从"一"查"多": 一方的实例化对象.多方模型类名小写_set.all()
# 查询1号学校所有的学生: school = School.objects.get(id=1) students = school.student_set.all()
从"多"查"一",通过“多”方设置的关联类属性查询
#查询6号学生对应的学校: student = Student.objects.get(id=6) school = student.school
6.5、多对多
6.5.1、实现方式
在某个"多"方使用ManyToManyField,关联另一个"多"方和第三方模型维护关系
使用外键实现,原表是没有任何变化的,是多出一张专门用来维护关系的表,关系表中通过两个外键实现对两张表的关联
# 系统会自动生成第三方表 from django.db import models class Member(models.Model): # 成员模型 name = models.CharField(max_length=20) age = models.IntegerField() sex = models.CharField(max_length=10) def __str__(self): return self.name class Community(models.Model): # 社团模型 name = models.CharField(max_length=20) buildtime = models.DateField() members = models.ManyToManyField(Member) def __str__(self): return self.name
# 可以指定生成第三方表 from django.db import models class Member(models.Model): # 成员模型 name = models.CharField(max_length=20) age = models.IntegerField() sex = models.CharField(max_length=10) def __str__(self): return self.name class Community(models.Model): # 社团模型 name = models.CharField(max_length=20) buildtime = models.DateField() members = models.ManyToManyField(Member,through="Relation") def __str__(self): return self.name class Relation(models.Model): member = models.ForeignKey(Member,on_delete=models.CASCADE) community = models.ForeignKey(Community,on_delete=models.CASCADE) join_reason = models.CharField(max_length=100)
6.5.2、对象间的关联
①系统自动生成第三方表关联数据
# 创建Member对象: m1 = Member.objects.create(name='马小跳',age=20,sex='男') m2 = Member.objects.create(name='李丽丽',age=25,sex='女') m3 = Member.objects.create(name='黄大牛',age=35,sex='男') # 创建Community对象: c1 = Community.objects.create(name='天涯吉他社',buildtime=date(2016,6,6)) c2 = Community.objects.create(name='读书会',buildtime=date(2017,10,1)) c3 = Community.objects.create(name='瑜伽协会',buildtime=date(2008,9,3)) # 不维护关系一方属性.维护关系一方类名小写_set.add(维护关系一方的对象) m1.community_set.add(c1) # 马小跳加入天涯吉他社 m1.community_set.add(c2) # 马小跳加入读书会
②通过第三方模型对象维护
#创建Member对象: member1 = Member.objects.create(name='马小跳',age=20,sex='男') member2 = Member.objects.create(name='李丽丽',age=25,sex='女') member3 = Member.objects.create(name='黄大牛',age=35,sex='男') #创建Community对象: community1 = Community.objects.create(name='天涯吉他社',buildtime=date(2016,6,6)) community2 = Community.objects.create(name='读书会',buildtime=date(2017,10,1)) community3 = Community.objects.create(name='瑜伽协会',buildtime=date(2008,9,3)) # 创建Relation关系对象,通过实例化对象关联: r1 = Relation.objects.create(member=member2,community=community1,join_reason='好玩') # 通过外键值关联: r2 = Relation.objects.create(member_id=member3.id,community_id=community1.id,join_reason='交朋友')
6.5.3、查询
①从主动维护关系的"多"方查询:实例化对象.通过关联的类属性.all()
#查询3号社团所有的成员: #方式一:借助关联的类属性直接查询 all_members = community.members.all() for m in all_members: print(m) #方式二:借助中间模型查询: community = Community.objects.get(id=3) relations = Relation.objects.filter(community=community) for r in relations: print(r.member)
②从不负责维护关系的"多"方查询:实例化对象.对方模型类名小写_set.all()
查询1号成员参加的社团: member1 = Member.objects.get(id=1) all_community = member1.community_set.all() for c in all_community: print(c)
6.6、模型外键自关联
场景:在需要引用自身模型的时候
6.6.1、"一对多"自引用
models.ForeignKey('self',on_delete=models.CASCADE,null=True)
from django.db import models class Emp(models.Model): name = models.CharField(max_length=20) age = models.IntegerField() sex = models.CharField(max_length=10) salary = models.FloatField() manager = models.ForeignKey('self',on_delete=models.CASCADE,null=True) # 外键自引用 创建最高领导boss和其他员工: boss = Emp.objects.create(name='马云',age=55,sex='男',salary=3000.5) emp1 = Emp.objects.create(name='马晓云',age=25,sex='男',salary=2000,manager=boss) emp2 = Emp.objects.create(name='张勇',age=58,sex='男',salary=6000,manager=boss) emp3 = Emp.objects.create(name='张三',age=58,sex='男',salary=3000,manager=emp2) emp4 = Emp.objects.create(name='张丽',age=28,sex='女',salary=3000,manager=emp3) emp5 = Emp.objects.create(name='张小勇',age=22,sex='男',salary=3000,manager=emp2) 查询id为3的员工的直接下属与直属上司: emp = Emp.objects.get(id=3) workers = emp.emp_set.all() # 直属下属 leader = emp.manager 级联删除所有员工: boss.delete()
6.6.2、“一对一”自引用
models.OneToOneField('self',on_delete=models.CASCADE,null=True)
from django.db import models class Spy(models.Model): name = models.CharField(max_length=20) age = models.IntegerField() sex = models.CharField(max_length=10) manager = models.OneToOneField('self',on_delete=models.CASCADE,null=True) class Meta: db_table = 'spies' 创建Spy对象boss,并创建其他对象 boss = Spy.objects.create(name='戴笠',age=45,sex='男') spy1 = Spy.objects.create(name='毛人凤',age=30,sex='男',manager=boss) spy2 = Spy.objects.create(name='张三',age=25,sex='男',manager=spy1) 查询boss的直属下属的名字: boss.spy.name
6.7、数据删除 on_delete
①、models.CASECADE
主表删除,从表自动删除(级联数据)
从表数据删除,主表不受影响
②、models.PROTECT
开发中为了防止误操作,我们通常会设置为此模式,
主表如果存在级联数据,删除动作受保护,不能成功
主表不存在级联数据,可以删除成功
③、SET
SET_NULL,主表删除时,从表的外键列值为null,注:必须设置null=True
SET_DEFAULT 存在默认值
指定值
6.8、模型的面向对象
默认继承是会将通用字段放到父表中,特定字段放在自己的表中,中间使用外键连接
关系型数据库关系越复杂,效率越低,查询越慢
父类表中也会存储过多的数据
使用元信息来解决这个问题
使模型抽象化,抽象的模型就不会在数据库中产生映射了.子模型映射出来的表直接包含父模型的字段
class Animal(models.Model): a_name = models.CharField(max_length=16) class Meta: abstract = True class Cat(Animal): c_eat = models.CharField(max_length=32)
6.9补充
在企业开发中,model--->sql,我们可以使用django_migrations
同样的sql--->model,在Django中也同样支持
python manage.py inspectdb
元信息中包含一个属性 manage=False
如果自己的模型不想被迁移系统管理,也可以使用 manage=False进行声明