django文档6-6.8
## 6.5、创建关联模型
````sql
-- 版本1
-- student
id name age class_name class_tutor class_num
1 rain 22 s12 zhangsan 78
2 alvin 24 s12 zhangsan 78
3 eric 23 s12 zhangsan 78
-- 版本2: 一对多是通过在多的表中创建关联字段
-- student
id name age class_id
1 rain 22 1
2 alvin 24 1
3 eric 23 1
-- class
id class_name class_tutor class_num
1 s12 zhangsan 78
2 s13 lisi 88
-- 查询学生rain所在班级名称
select class_id from student where name = 'rain' -- 1
select class_name from class where id = 1
-- 多对多的关系的确立是通过创建第三张关系表来完成的
-- course(选秀课程)
id name
1 python
2 java
3 php
-- student2course
id student_id course_id
1 1 1 -- id为1的学生选修了id为1的课程
2 1 2 -- id为1的学生选修了id为2的课程
3 2 2 -- id为2的学生选修了id为2的课程
-- 一对一 ,类似一对多,在两张关系表中的任何一张都可以建立一个关联字段
-- student
id name age class_id student_detail_id(unique)
1 rain 22 1 1
2 alvin 24 1 3
3 eric 23 1 2
-- student_detail
id addr email tel
1 bj 123 110
2 bj 234 911
3 nj 456 112
````
实例:我们来假定下面这些概念,字段和关系
> - 班级模型: 班级名称、导员。
> - 课程模型:课程名称、讲师等。
> - 学生模型: 学生有姓名,年龄,只有一个班级,所以和班级表是一对多的关系(one-to-many);选修了多个课程,所以和课程表是多对多的关系(many-to-many)
> - 学生详情:学生的家庭地址,手机号,邮箱等详细信息,和学生模型应该是一对一的关系(one-to-one)
模型建立如下:
```python
from django.db import models
# Create your models here.
class Clas(models.Model):
name = models.CharField(max_length=32, unique=True, verbose_name="班级名称")
class Meta:
db_table = "db_class"
class Course(models.Model):
name = models.CharField(max_length=32, unique=True, verbose_name="课程名称")
class Meta:
db_table = "db_course"
class Student(models.Model):
sex_choices = (
(0, "女"),
(1, "男"),
(2, "保密"),
)
name = models.CharField(max_length=32, unique=True, verbose_name="姓名")
age = models.SmallIntegerField(verbose_name="年龄", default=18) # 年龄
sex = models.SmallIntegerField(choices=sex_choices)
birthday = models.DateField()
# 一对多
# on_delete= 关联关系的设置
# models.CASCADE 删除主键以后, 对应的外键所在数据也被删除
# models.DO_NOTHING 删除主键以后, 对应的外键不做任何修改
# 反向查找字段 related_name
clas = models.ForeignKey("Clas", on_delete=models.CASCADE)
# 多对多
# 建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
courses = models.ManyToManyField("Course", db_table="db_student2course")
# 一对一,使用同一对多
stu_detail = models.OneToOneField("StudentDetail", on_delete=models.CASCADE)
class Meta:
db_table = "db_student"
def __str__(self):
return self.name
class StudentDetail(models.Model):
tel = models.CharField(max_length=32)
email = models.EmailField()
description = models.TextField(null=True, verbose_name="个性签名")
class Meta:
db_table = "db_student_detail"
```
## 6.6、关联添加
#### (1)一对多与一对一
```python
stu = Student.objects.create(name="王五", age=23, sex=1, birthday="1991-11-12", clas_id=9, stu_detail_id=6)
print(stu.name)
print(stu.age)
print(stu.sex)
print(stu.clas_id) # 6
print(stu.stu_detail_id) # 5
print(stu.clas) # 模型类对象
print(stu.stu_detail) # 模型类对象
# 查询stu这个学生的班级名称
print(stu.clas.name)
# 查询stu这个学生的手机号
print(stu.stu_detail.tel)
```
#### (2)多对多
```python
# stu = Student.objects.create(name="rain", age=33, sex=1, birthday="1996-11-12", clas_id=9, stu_detail_id=7)
# (1) 添加多对多的数据
# 添加多对多方式1
c1 = Course.objects.get(title="思修")
c2 = Course.objects.get(title="逻辑学")
stu.courses.add(c1,c2)
# 添加多对多方式2
stu = Student.objects.get(name="张三")
stu.courses.add(5,7)
# 添加多对多方式3
stu = Student.objects.get(name="李四")
stu.courses.add(*[6,7])
# (2) 删除多对多记录
stu = Student.objects.get(name="李四")
stu.courses.remove(7)
# (3) 清空多对多记录:clear方法
stu = Student.objects.get(name="rain")
stu.courses.clear()
# (4) 重置多对多记录:set方法
stu = Student.objects.get(name="李四")
stu.courses.set([5,8])
# (5) 多对多记录查询: all
# 查询李四所报课程的名称
stu = Student.objects.get(name="李四")
courses = stu.courses.all()
courses = stu.courses.all().values("title")
print(courses) # <QuerySet [<Course: Course object (5)>, <Course: Course object (8)>]>
```
## 6.7、关联查询
#### 6.7.1、基于对象查询(子查询)
```python
# ********************************** 一对多查询
# 查询张三所在班级的名称
# stu = Student.objects.get(name="张三")
# print(stu.clas.name)
# 查询计算机科学与技术2班有哪些学生
# clas = Clas.objects.get(name="计算机科学与技术2班")
# 反向查询方式1:
# ret = clas.student_set.all() # 反向查询按表名小写_set
# print(ret) # <QuerySet [<Student: 张三>, <Student: 李四>]>
# 反向查询方2:
# print(clas.student_list.all()) # <QuerySet [<Student: 张三>, <Student: 李四>]>
# ********************************** 一对一查询
# 查询李四的手机号
# stu = Student.objects.get(name="李四")
# print(stu.stu_detail.tel)
# 查询110手机号的学生姓名和年龄
# stu_detail = StudentDetail.objects.get(tel="110")
# 反向查询方式1: 表名小写
# print(stu_detail.student.name)
# print(stu_detail.student.age)
# 反向查询方式2: related_name
# print(stu_detail.stu.name)
# print(stu_detail.stu.age)
# ********************************** 多对多查询
# 查询张三所报课程的名称
# stu = Student.objects.get(name="张三")
# print(stu.courses.all()) # QuerySet [<Course: 近代史>, <Course: 篮球>]>
# 查询选修了近代史这门课程学生的姓名和年龄
# course = Course.objects.get(title="近代史")
# 反向查询方式1: 表名小写_set
# print(course.student_set.all()) # <QuerySet [<Student: 张三>, <Student: 李四>]>
# 反向查询方式2:related_name
# print(course.students.all())
# print(course.students.all().values("name","age")) # <QuerySet [{'name': '张三', 'age': 22}, {'name': '李四', 'age': 24}]>
```
> (1)正向查询按字段
>
> (2)反向查询按表名小写或者related_name
#### 6.7.2、基于双下划线查询(join查询)
```python
# 查询张三的年龄
ret = Student.objects.filter(name="张三").values("age")
print(ret) # <QuerySet [{'age': 22}]>
# (1) 查询年龄大于22的学生的姓名以及所在名称班级
# select db_student.name,db_class.name from db_student inner join db_class on db_student.clas_id = db_class.id where db_student.age>22;
# 方式1 : Student作为基表
ret = Student.objects.filter(age__gt=22).values("name","clas__name")
print(ret)
# 方式2 :Clas表作为基表
ret = Clas.objects.filter(student_list__age__gt=22).values("student_list__name","name")
print(ret)
# (2) 查询计算机科学与技术2班有哪些学生
ret = Clas.objects.filter(name="计算机科学与技术2班").values("student_list__name")
print(ret) #<QuerySet [{'student_list__name': '张三'}, {'student_list__name': '李四'}]>
# (3) 查询张三所报课程的名称
ret = Student.objects.filter(name="张三").values("courses__title")
print(ret) # <QuerySet [{'courses__title': '近代史'}, {'courses__title': '篮球'}]>
# (4) 查询选修了近代史这门课程学生的姓名和年龄
ret = Course.objects.filter(title="近代史").values("students__name","students__age")
print(ret) # <QuerySet [{'students__name': '张三', 'students__age': 22}, {'students__name': '李四', 'students__age': 24}]>
# (5) 查询李四的手机号
ret = Student.objects.filter(name='李四').values("stu_detail__tel")
print(ret) # <QuerySet [{'stu_detail__tel': '911'}]>
# (6) 查询手机号是110的学生的姓名和所在班级名称
# 方式1
ret = StudentDetail.objects.filter(tel="110").values("stu__name","stu__clas__name")
print(ret) # <QuerySet [{'stu__name': '张三', 'stu__clas__name': '计算机科学与技术2班'}]>
# 方式2:
ret = Student.objects.filter(stu_detail__tel="110").values("name","clas__name")
print(ret) # <QuerySet [{'name': '张三', 'clas__name': '计算机科学与技术2班'}]>
```
> (1)正向关联按关联字段
>
> (2)反向按表名小写或related_name
#### 6.7.3、关联分组查询
````python
# from django.db.models import Avg, Count, Max, Min
ret = Student.objects.values("sex").annotate(c = Count("name"))
print(ret) # <QuerySet [{'sex': 0, 'c': 1}, {'sex': 1, 'c': 3}]>
# (1)查询每一个班级的名称以及学生个数
ret = Clas.objects.values("name").annotate(c = Count("student_list__name"))
print(ret) # <QuerySet [{'name': '网络工程1班', 'c': 0}, {'name': '网络工程2班', 'c': 0}, {'name': '计算机科学与技术1班', 'c': 0}, {'name': '计算机科学与技术2班', 'c': 1}, {'name': '软件1班', 'c': 3}]>
# (2)查询每一个学生的姓名,年龄以及选修课程的个数
ret = Student.objects.values("name","age").annotate(c=Count("courses__title"))
print(ret) # <QuerySet [{'name': 'rain', 'c': 0}, {'name': '张三', 'c': 2}, {'name': '李四', 'c': 2}, {'name': '王五', 'c': 0}]>
ret = Student.objects.all().annotate(c=Count("courses__title")).values("name","age","sex","c")
# print(ret)
# (3) 每一个课程名称以及选修学生的个数
ret = Course.objects.all().annotate(c = Count("students__name")).values("title","c")
print(ret) # <QuerySet [{'title': '近代史', 'c': 2}, {'title': '思修', 'c': 0}, {'title': '篮球', 'c': 1}, {'title': '逻辑学', 'c': 1}, {'title': '轮滑', 'c': 0}]>
# (4) 查询选修课程个数大于1的学生姓名以及选修课程个数
ret = Student.objects.all().annotate(c=Count("courses__title")).filter(c__gt=1).values("name","c")
print(ret) # <QuerySet [{'name': '张三', 'c': 2}, {'name': '李四', 'c': 2}]>
# (5) 查询每一个学生的姓名以及选修课程个数并按着选修的课程个数进行从低到高排序
ret = Student.objects.all().annotate(c=Count("courses__title")).order_by("c").values("name","c")
print(ret)
````
## 6.8、项目练习
