写该篇博客的原因
目前在学习django,django的orm非常好用,但是网上找的orm的操作写的普遍不是很全面,找了几篇都不是很清晰,所以自己整理一下(多对多后面会更新)
单表数据库模型
# 这个类在models.py(模型)文件中
class Users(models.Model):
SEX = ((0,'man'),(1,'woman'))
# 后面sex字段会用到这个
id = models.AutoField(primary_key=True)
# 不加这个id字段的话就会自己生成一个自增主键,字段名也是id,所以这个字段可写可不写
name = models.CharField(max_length=50, blank=True, null=True, verbose_name='姓名')
# verbose_name属性就是admin后台管理显示的字段名,如果不加的话,admin后台默认就是英文的字段(name)
# 注意null和blank的区别: null=True表示在数据库层面可为空,而blank=True则是在django层面限制可为空,使用时两者是连用的,否则数据库可空但django不能为空,还是会报错的,反之亦然
age = models.IntegerField(blank=True, null=True, verbose_name='年龄')
phone = models.CharField(max_length=11, default='', blank=True, verbose_name='手机号')
# blank=True,就是表单可为空,所以数据表该字段给一个 default='' ,表示该字段默认为空
sex = models.CharField(max_length=50, blank=True, null=True, verbose_name='性别', choices=Sex)
# choices属性是‘选择’属性,存0就代表男,存1就代表女,admin后台管理页面就显示一个下拉框,让用户选择男女,选完之后按照0,1存数据库中。可以用obj.get_字段名称_display拿到0,1对应的‘男’,‘女’。
def __str__(self):
return self.name
class Meta:
db_table = 'users' # 在数据库中的表名
ordering = ['-age', 'name'] # 年龄倒叙,名字正序,排序会增加数据库负担
增
# 下面的语句写在views.py(视图)文件中
# 从模型文件models.py中导入所有的模型类
from models import *
# 增加有以下几种方法
# 方法一:
obj = Users()
obj.name = 'make'
obj.age = 18
....
obj.save() # 一定要执行save否则不会执行插入数据操作
# 方法二:
obj = Users(name="make",age=18,phone='133-8888-6666',sex=0)
obj.save()
# 方法三:
Users.objects.create(name="make",age=18,phone='133-8888-6666',sex=0)
# obj = Users.objects.create(name="make",age=18,phone='133-8888-6666',sex=0)
# create添加数据后会返回插入数据的类对象(存到了上面的obj变量中),通过这个类对象.属性(obj.name) 可以获取到对应字段的数据
# 方法四:
# 在管理器类中自己定义创建方方法:
# 创建一个自己的管理器,继承models.Manager
class myManager(models.Manager):
# 重写get_queryset方法,可以让返回的QuerySet(原始查询集)只包含sex=0(性别为男)的数据,所有基于QuerySet的查询方法都会被影响,例如.all(), .filter()...
def get_queryset(self):
return super().get_queryset().filter(sex=0)
# 也可以只重写某方法,例如重写all,可以让.all()方法在查询时只获得sex=0(性别为男)的数据
def all(self):
return super().all().filter(sex=0)
# 继承自己的管理器,定义了一个自己的管理器的create方法
class UsersManager(myManager):
def create(self,name,age,phone,sex):
obj = self.model()
obj.name = name
obj.age = age
obj.phone = phone
obj.sex = sex
return obj
# 把objects从默认的Manager()改成自己的UserManager()管理器
class Users(models.Model):
objects = UserManager()
# Users用create创建的时候可以不用以关键字参数传参了
obj = Users.objects.create('make',18,'133-8888-6666',0)
obj.save()
# 方法五:
# 在模型类(Users)中定义一个类方法用于创建对象
@classmethod
def create(cls, name, age, phone, sex):
return cls(name=name, phone=phone, sex=sex)
obj = Users.create('make',18,'133-8888-6666', 0)
obj.save()
查
在开查询之前,首先了解几个名词:
- 查询集:从数据库中获取数据的集合
- 原始查询集:调用get_queryset得到的查询集,不作为数据的提取
- 数据查询集合:在原始查询集或查询集上调用过滤器得到的数据查询集
- 过滤器:过滤器有返回查询集的过滤器,还有返回单个值得过滤器,见下方代码块注释。过滤器可以使用链式语法:Users.objects.all().filter().filter()
# 返回查询集的过滤器:all(), filter(), exclude(), older_by(), values()...
# 返回单个值得过滤器:get(), count(), first(), last(), exists()...
# all获取全部数据
uses = Users.objects.all()
# filter获取符合条件的全部数据,有两种写法
users = Users.objects.all().filter(name='make', age=18)
users = Users.objects.all().filter(name='make').filter(age=18)
# exclude返回不包含条件的数据集
users = Users.object.all().exclude(age=18) # 返回的结果集中不包括age=18的
# older_by根据字段进行排序
users = Users.objects.all().order_by('age') # 升序
users = Users.objects.all().order_by('-age') # 降序
# valuse一个对象是一个字典,构成一个列表返回
users = User.objects.values()
# values_list一个对象是一个元组,构成一个列表返回
users = User.objects.values_list()
# get获取单个满足条件的对象,如果没找到,则报 模型类.DoesNotExist异常,如果多条被返回则报 模型类.MultipleObjectsReturned, 均可通过try except捕获
user = Users.object.get(name='make')
# count返回查询集中的数据数量
user_num = Users.objects.all().count()
# first返回查询集中第一个对象
user = Users.objects.all().first()
# last返回查询集中最后一个对象
user = Users.objects.all.last()
# exists判断数据集中是否有数据,有返回True, 没有返回False
user_num = Users.objects.all().exists()
# 字段查询:实现where子句,作为filter,get等方法的参数
# 语法:属性名__比较运算符=值
# 对于外键:需要使用 外键属性_id 表示外键
# % 的使用:sql语句:where name like '%\%%' ===> orm语句: filter(name__contains='%')
# 比较运算符都有哪些:exact, contains, startswith, endswith, iexact, icontains, istartswith, iendswith, isnull, isnotnull, in, gt, gte, lt, lte, year, month, day, week_day, hour, minute, second...
改
# 改的三种方法
# 方法一: objects调用,下面示例是整列数据更新
Users.objects.update(name='make', age=18...)
# 方法二: queryset类型数据调用,下面也是更新整列数据
Users.objects.all().update(name='make', age=18...)
# 方法三:queryset类型数据调用,更新部分记录数据
obj = Book.objects.filter(name='make').update(name='bob')
print(obj) # 2 返回结果为受影响的行数
# 模型类对象不能直接调用update方法,错误示例如下
Users.objects.get(id=1).update(name='make')
# 报错信息'Book' object has no attribute 'update'
删
# delete()调用者可以是model对象,也可以是QuerySet类型数据
obj = Users.objects.get(id=3).delete()
print(obj) # (1, {'app01.Book': 1})
# 返回结果其实还是受影响的行数
obj = Users.objects.filter(name='make').delete()
print(obj) # (2, {'app01.Book': 2})
# 错误示例演示
obj = Users.objects.delete()
# 报错'Manager' object has no attribute 'delete'
# 控制器没有delete方法,原因就是怕你一下子全部删除了所有数据
动态操作
# 更新或创建,有就更新,没有就添加update_or_create()
Users.objects.update_or_create(
id=1,
defaults={
'name': 'make',
'age': 18
}
)
# 查询或创建,能够查询到就返回查询结果,查询不到就添加记录,查询时内部使用的还是get方法
Users.objects.get_or_create(
id=1,
defaults={
'name': 'make',
'age': 18
}
)
多表
一对一(OneToOne)数据库模型
# 声明关系的表为从表,OneToOne在哪张表声明都可以
# 一对一应用场景:
# 1. 一个用户对应一张身份证, 而身份证的信息不常用, 这个时候就可以将 " 身份证的信息抽取出来称为另一张表", 使两张表进行一对一的关联
# 2. 已经有一张表,想在不改变原表基础上增加新字段,也可以用一对一关联
class IdCard(models.Model):
idnum = models.CharField(max_lenght=20)
sex = models.BoolearnField()
class Meta:
db_table='IdCard'
class People(models.Model):
name = models.CharField(max_lenght=10)
card = models.OneToOneField(to='IdCard',to_field='id',on_delete=models.CASCADE)
# to 关联的表 to_field 关联的字段(默认关联那个表的id) on_delete 级联删除
class Meta:
db_table='people'
# on_delete的级联删除模式:
# 1. models.CASCADE 默认的模式,删除主表数据,从表中的一个外键被删除,删除从表数据,主表数据不动。
# 2. models.PROTECT 保护模式,从表中有数据时候无法删除主表数据,删除从表数据时候主表数据不动
# 3. models.SET_NULL 空值模式
# 4. models.SET_DEFAULT 默认值模式
# 5. models.SET() 动态设置模式,删除时动态指向一个实体访问
增
# 先创建一个主表对象
mycard = IdCard.objects.create(idmum='220303199912302222',sex=0)
# 再创建从表对象
People.objects.create(name='make', card=mycard)
查
# 从(People)获取主(IdCard)
# 格式:从表对象.外键属性.主表属性
people = People.objects.get(id=1)
print(people.card.idnum)
# 主(IdCard)获取从(People)
# 格式:主表对象.从表模型类名小写.从表属性
card = IdCard.objects.get(id=1)
print(card.people.name)
改
# 主表的改法和单表的没区别
mycard = IdCard.objects.get('id'=1)
mycard.sex=1
mycard.save()
# 从表的改法:
mycard = IdCard.objects.create(idnum='220303199912302222', sex=1) # 主表对象
# save()方法
people = People.objects.get(id=1)
people.name = 'make'
people.card = mycard # 从表对象.onetoone字段 = 主表对象
people.save()
# 注意:update()是QuerySet的方法
people_QS = People.objects.filter(name='make') # filter获得的从表对象是QuerySet类型
people_QS.update(name='bob', card=mycard)
删
# 删除的时候和单表没什么区别,直接删除就行,但是模式要看on_delete级联删除怎么设置的
People.objects.get(name='make').delete()
IdCard.objects.get(id=1).delete()
IdCard.objects.all().delete() # 清空一整张表
一对多/多对一(foreginkey)数据库模型
# 声明关系的表为从表,一对多的foreginkey写在多的那边,假如一个班级对应多个学生,那么foreginkey就写在学生表中,学生表就为从表
# related_name:反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
# related_query_name:反向查询操作时,使用的连接前缀,用于替换表名。
class Grade(models.Model):
grade_name = models.CharField(max_lenght=20)
class Meta:
db_table='grade'
class Students(models.Model):
name = models.CharField(max_lenght=10)
grade = models.ForeginKey(to='Grade',to_field='id',on_delete=models.CASCADE)
增
# 方法一 和一对一相同
grade_obj = models.Grade.objects.get(id=1) # 班级(主表)对象
models.Students.objects.create(name='make', grade=grade_obj)
# 方法二
models.Students.objects.create(name='make', grade_id=1) # 外键字段_id 班级的id值)
查
# 从(Students)获取主(Grade) ==> 正向查询
# 格式:从表对象.外键属性.主表属性
# 已知一个学生,获取该学生所在的班级
stu = Studens.objects.get(id=1)
print(stu.grade.grade_name)
# 主(Grade)获取从(Students) ==> 反向查询
# 格式:主表对象.从表类模型小写_set.过滤器
# 已知一个班级,查班级下的全部学生
grade = Grade.objects.get(id=1)
stus = grade.students_set.all()
print(stus)
# 查到grade_name值为一年一班的id
grade = Grade.objects.fliter(grade_name="一年一班").values(id)
# 通过grade得到的id,去到Students中查到这个班级id对应的学生姓名
stus = Students.objects.filter(grade=grade).values("name")
改
# 和一对一没区别
grade = Grade.objects.get('id'=1)
grade.grade_name='一年一班'
grade.save()
# 从表的改法:
grade = Grade.objects.create(grade_name='一年一班') # 主表对象
# save()方法
stu = Students.objects.get(id=1)
stu.name = 'make'
stu.grade = grade # 从表对象.onetoone字段 = 主表对象
stu.save()
# 注意:update()是QuerySet的方法
students = Students.objects.filter(name='make') # filter获得的从表对象是QuerySet类型
students.update(name='bob', grade=grade)
删
# 和一对一的删除没区别
Grade.objects.get(name='一年一班').delete()
Students.objects.get(id=1).delete()
Students.objects.filter(name='make').delete()