模型(Model)设计-数据访问操作(记录查询-跨表)
回顾
a、模型(Model)设计流程过程说明
b、数据访问操作基本API
目标
前面已经介绍了如何快速入门,现在开始熟练掌握一些知识。前面我们已经定义了数据模型,接下来,我们通过数据模型,并对数据进行增删改查,而进行增删改查操作,我们一般是在视图逻辑中进行操作。
- 掌握定义模型类-数据访问操作(记录查询-跨表)
一、跨表记录查询
关键点:正向查询按字段,反向查询按表名。
注意表名为对应模型类名称的小写
1.1、一对一关系的跨表记录查询操作
1.1.1、表结构与数据示例
1.1.2、模型代码示例
class Teacher(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10, verbose_name="教师名称")
class Meta:
db_table = 't_teacher' # 定义表名为t_teacher
def __str__(self):
# 默认显示类的name字段
return self.name
class TeacherDetail(models.Model):
id = models.AutoField(primary_key=True)
telephone = models.CharField(verbose_name='手机号', max_length=11)
teacher_id = models.OneToOneField(to='Teacher', to_field='id', db_column='teacher_id', on_delete=models.CASCADE)
class Meta:
db_table = 't_teacher_detail' # 定义表名为t_teacher
def __str__(self):
# 默认显示类的name字段
return self.id
代码说明:
可以看出 Teacher和TeachDetail是一对一的关系,由于OneToOneField 定义在TeacherDetail中。故:
- 正向查询:通过 t_teacher_detail 表信息查询 t_teacher 表的信息为正向查询。
- 反向查询:通过 t_teacher 表信息查询 t_teacher_detail 表的信息为反向查询。
1.1.3、查询代码示例
a、基于对象的跨表查询
# 正向查询-查询手机号为13000000001的老师名字
teacher_detail_obj = TeacherDetail.objects.filter(telephone="13000000001").first()
teacher_name = teacher_detail_obj.teacher_id.name
print(teacher_name)
# 反向查询-查询老师名为张三的手机号
teacher_obj = Teacher.objects.filter(name="张三").first()
telephone = teacher_obj.teacherdetail.telephone
print(telephone)
执行结果
张三
13000000001
b、基于下划线的跨表查询
Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。
# 正向查询-查询手机号为13000000001的老师名字
qs_001 = TeacherDetail.objects.filter(telephone="13000000001").values_list("teacher_id__name").first()
print(qs_001)
teacher_name = qs_001[0]
print(teacher_name)
# 反向查询-查询老师名为张三的手机号
qs_002 = Teacher.objects.filter(name="张三").values_list("teacherdetail__telephone").first()
print(qs_002)
telephone = qs_002[0]
print(telephone)
执行结果
('张三',)
张三
('13000000001',)
13000000001
1.2、一对多关系的跨表记录查询操作
1.2.1、表结构与数据示例
1.2.2、模型代码示例
class Teacher(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10, verbose_name="教师名称")
class Meta:
db_table = 't_teacher' # 定义表名为t_teacher
def __str__(self):
# 默认显示类的name字段
return self.name
class Course(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=16, verbose_name="课程名称", unique=True)
teacher_id = models.ForeignKey(to='Teacher', to_field='id', on_delete=models.CASCADE, db_column="teacher_id")
class Meta:
db_table = 't_course'
def __str__(self):
# 默认显示类的name字段
return self.name
代码说明:
可以看出 Teacher 和 Course 是一对一的关系,由于 ForeignKey 定义在 Course 中。故:
- 正向查询:通过 t_course 表信息查询 t_teacher 表的信息为正向查询。
- 反向查询:通过 t_teacher 表信息查询 t_course 表的信息为反向查询。
1.2.3、查询代码示例
a、基于对象的跨表查询
# 基于对象的联表查询
# 正向查询-查询课程为生物的执教老师名称
course_obj = Course.objects.filter(name="生物").first()
teacher_name = course_obj.teacher_id.name
print(teacher_name)
# 反向查询-查询张三老师所交的课
teacher_obj = Teacher.objects.filter(name="张三").first()
course_qs = teacher_obj.course_set.all()
print(course_qs)
执行结果:
张三
<QuerySet [<Course: 生物>, <Course: 体育>]>
代码说明:
- 重点1-反向查询用表名:
teacher_obj.course_set.all()
反向查询用表名,表名为对应定义模型类的小写,即course
。 - 重点2-查询对应多条:因为是一对多关系,加上
_set.all()
。获取对应的多条数据。
b、基于下划线的跨表查询
# 基于下划线的跨表查询
# 正向查询-查询课程为生物的执教老师名称
teacher_qs = Course.objects.filter(name="生物").values("teacher_id__name")
print(teacher_qs)
# 反向查询-查询张三老师所交的课
course_qs = Teacher.objects.filter(name="张三").values("course__name")
print(course_qs)
执行结果:
<QuerySet [{'teacher_id__name': '张三'}]>
<QuerySet [{'course__name': '生物'}, {'course__name': '体育'}]>
代码说明:
- 重点1-跨表查询在 values 方法中,通过对应字段和表名的双划线表示查询对应字段。比如代码中的
teacher_id__name
和course__name
。
1.3、多对多关系的跨表记录查询操作
1.3.1、表结构与数据示例
1.3.2、模型代码示例
class Teacher(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10, verbose_name="教师名称")
class Meta:
db_table = 't_teacher' # 定义表名为t_teacher
def __str__(self):
# 默认显示类的name字段
return self.name
class Class(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=16, verbose_name="编辑名称", unique=True)
grade_id = models.ForeignKey(to='Grade', to_field='id', on_delete=models.CASCADE, db_column="grade_id")
teacher_id = models.ManyToManyField(to='Teacher', db_column="teacher_id", db_table="t_teach2cls")
class Meta:
db_table = 't_class'
def __str__(self):
# 默认显示类的name字段
return self.name
代码说明:
可以看出 Teacher 和 Class 是多对多的关系,由于 ManyToManyField 定义在 Class 中。故:
- 正向查询:通过 t_class 表信息查询 t_teacher 表的信息为正向查询。
- 反向查询:通过 t_teacher 表信息查询 t_class 表的信息为反向查询。
1.2.3、查询代码示例
a、基于对象的跨表查询
# 基于对象的联表查询
# 正向查询-查询一年级的执教老师
class_obj = Class.objects.filter(name="一年一班").first()
teacher_qs = class_obj.teacher_id.all()
print(teacher_qs)
# 反向查询-查询张三老师的所教的班级
teacher_obj = Teacher.objects.filter(name="张三").first()
class_qs = teacher_obj.class_set.all()
print(class_qs)
执行结果:
<QuerySet [<Teacher: 张三>, <Teacher: 李四>]>
<QuerySet [<Class: 一年一班>, <Class: 一年二班>]>
b、基于下划线的跨表查询
# 基于下划线的跨表查询
# 正向查询-查询一年级的执教老师
teacher_qs = Class.objects.filter(name="一年一班").values("teacher_id__name")
print(teacher_qs)
# 反向查询-查询张三老师的所教的班级
course_qs = Teacher.objects.filter(name="张三").values("class__name")
print(course_qs)
执行结果:
<QuerySet [{'teacher_id__name': '张三'}, {'teacher_id__name': '李四'}]>
<QuerySet [{'class__name': '一年一班'}, {'class__name': '一年二班'}]>
二、补充知识
2.1、基于对象的跨表反向查询自定义
前面我们介绍了,基于对象的跨表反向查询,默认是使用 模型类(小写)_set
,其实我们也可以自定义。
- 你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写
XXX_set
的名称
举个列子,
class Teacher(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10, verbose_name="教师名称")
class Meta:
db_table = 't_teacher' # 定义表名为t_teacher
def __str__(self):
# 默认显示类的name字段
return self.name
class Course(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=16, verbose_name="课程名称", unique=True)
teacher_id = models.ForeignKey(to='Teacher', to_field='id', on_delete=models.CASCADE, db_column="teacher_id", related_name='teacherSet')
class Meta:
db_table = 't_course'
def __str__(self):
# 默认显示类的name字段
return self.name
代码说明:
- 现在我们在查询的时候就不是使用
_set
进行反向查询,而是用定义的related_name 对应的值进行。如下示例:
# 反向查询-查询张三老师所交的课
teacher_obj = Teacher.objects.filter(name="张三").first()
course_qs = teacher_obj.teacherSet.all()
print(course_qs)
- 注意一但你在模型类的关系字段中设置了related name, 你将不能再通过_set方法来反向查询.