一对一关系类型
OneToOneFiled 继承自 ForeignKey ,在概念上,它类似 unique=Ture 的 ForeignKey ,它与 ForeignKey 最显著的区别在于反向查询上,ForeignKey 反向查询返回的是一个QuerySet对象(一个对象实例列表),含两个或两个以上模型数据对象;而 OneToOneFiled 反向查询返回的是一个对象实例(单个模型数据对象)。
一对关系类型的使用和场景相对其他两种关联关系要少,经常用于对已有 Model 的扩展,例如我们可以对 Teacher 表进行扩展,添加类似用户手机号、地址、邮箱等字段。此时就可以新建一个 Model ,命名为TeacherInfo ,并定义一个字段与 Teacher 表一对一关联。这样就实现了教师信息拓展表TeacherInfo 与教师Teacher 表一对一关联
语法格式
class A(model.Model):
...
class B(model.Model):
属性 = models.OneToOneField(A)
模型创建
在 Lecture 应用目录下的 models.py 文件中,创建一个模型,命名为 Teacher ,再创建一个 TeacherInfo 模型用于记录教师信息
class Teacher(models.Model):
name = models.CharField(max_length=100, null=False)
college = models.CharField(max_length=256)
major = models.CharField(max_length=256)
def __str__(self):
return self.name
class TeacherInfo(models.Model):
teacher = models.OneToOneField(Teacher,on_delete=models.CASCADE)
identityCard = models.CharField(max_length=18,unique=True)
phone = models.CharField(max_length=11, unique=True)
email = models.EmailField()
officeAddress = models.CharField(max_length=256)
老师的数据信息是一对一,学生的数据信息也可以是一对一
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=100, null=False)
college = models.CharField(max_length=256)
major = models.CharField(max_length=256)
grade = models.CharField(max_length=256)
def __str__(self):
return self.name
class StudentInfo(models.Model):
student = models.OneToOneField(Student,on_delete=models.CASCADE)
identityCard = models.CharField(max_length=18,unique=True)
phone = models.CharField(max_length=11, unique=True)
email = models.EmailField()
Model的继承模型
在 Django 中每个 Model 都是一个 Pyhton 类,Django Model 的继承与 Python 类的继承是一样的,只是 Django 要求所有自定义的 Model 都必须继承自 django.db.models.Model 。通过类之间的继承 Django 会对自定义的 Model 自动添加了两个属性分别是 id 和 objects。
对比 Student 类和 Teacher 类,都拥有相同的字段:姓名、学院、专业,而 Student 类比 Teacher 类多了一个班级字段;对比 StudentInfo 类和 TeacherInfo 类,都拥有相同的字段:身份证号、手机号、邮箱,而 TeacherInfo 类比 StudentInfo 类多了一个办公地址字段;对于这些相同的内容,是可以提取出来的,将这些字段统一定义在抽象基类中,避免去重复定义这些字段
class SchoolMember(models.Model):
name = models.CharField(max_length=100, null=False)
college = models.CharField(max_length=256)
major = models.CharField(max_length=256)
class Meta:
abstract = True
class Student(SchoolMember):
grade = models.CharField(max_length=256)
def __str__(self):
return self.name
class Teacher(SchoolMember):
def __str__(self):
return self.name
class SchoolMemberInfo(models.Model):
identityCard = models.CharField(max_length=18, unique=True)
phone = models.CharField(max_length=11, unique=True)
email = models.EmailField()
class Meta:
abstract = True
class StudentInfo(SchoolMemberInfo):
student = models.OneToOneField(Student,on_delete=models.CASCADE)
class TeacherInfo(SchoolMemberInfo):
teacher = models.OneToOneField(Teacher, on_delete=models.CASCADE)
officeAddress = models.CharField(max_length=256)
SchoolMember 、Student 和 Teacher 3 个类映射到数据库后,只会被定义为两个数据表。 分别是 Student 与 Teacher 。 它们都继承自 SchoolMember ,且继承了父表中的所字段值,同时自身又自定义了新的字段。所以,它们对应的字段分别如下所示:
Student 数据表:有 id、name、college、major 和 grade 5 个字段;Teacher 数据表:有 id、name、college和major 4个字段。
关于 Model 的元数据继承关系,遵循以下几个规则:
- 抽象基类中定义的元数据,子类中没有定义,子类会继承基类中的元数据;
- 抽象基类中定义的元数据,子类也定义了,子类优先级更高;
- 子类可以定义自己的元数据,即不出现在抽象基类中的元数据。
在定义抽象基类时,需要注意,如果定义了 ForeignKey 或 ManyToManyField 类型的字段,并且设置了 related_name 或者 related_query_name 参数,由于继承关系,子类也会拥有同样的字段,所以,在子类中的反向名称和查询名称是唯一的。
路由、视图与模板
在lecture应用下的urls.py 文件中新增子路由
from django.urls import path
from lecture import views as lecture_view
urlpatterns = [
path('Teachers/',lecture_view.Teachers),
path('Teachers/<int:teacher_id>',lecture_view.Teachers_detail),
]
在lecture应用目录下的views.py 文件中新增Teachers 和Teachers_detail 两个视图函数
def Teachers(request):
Teacher_list = Teacher.objects.all()
return render(request,'Teachers.html',{'Teacher_list':Teacher_list})
def Teachers_detail(request,teacher_id):
try:
teacher = Teacher.objects.get(id=teacher_id)
except:
return render(request,'404.html')
return render(request,'Teacher_detail.html',{'teacher':teacher})
将数据返回给前端模板文件
Teachers.html
{% extends "base.html" %}
{% block title%}讲座讲师管理{% endblock %}
{% block content%}
<ul class="list-group">
{% for Teacher in Teacher_list %}
<li class="list-group-item text-center">
<a href="/lecture/Teachers/{{ Teacher.id }}">{{ Teacher }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}
Teacher_detail.html
{% extends 'base.html' %}
{% block title%}讲座老师详情页{% endblock %}
{% block content%}
<div class="panel panel-info">
<div class="panel-heading"> 讲座老师详情页 </div>
<div class="panel-body">
<p>姓名:{{ teacher.name }}</p>
<p>手机号:{{ teacher.teacherinfo.phone }}</p>
<p>邮箱:{{ teacher.teacherinfo.email }}</p>
<p>办公地点:{{ teacher.teacherinfo.officeAddress }}</p>
<p>关联讲座:
{% for lecture in teacher.studylecture_set.all %}
<a href="/lecture/lectures/{{ lecture.id }}">{{ lecture }}</a>
{% endfor %}
</p>
<p><a href="/lecture/Teachers/" class="btn btn-info">返回讲师列表</a></p>
</div>
</div>
{% endblock %}
这里关联讲座,用到了多对一外键关联查询中的反向查询,通过老师去查询关联的讲座。teacher.studylecture_set.all 返回一个QuerySet对象(一个对象实例列表):
<QuerySet [<StudyLecture: 易经学习>, <StudyLecture: 历史讲座>]> ,里面有<StudyLecture: 易经学习>, <StudyLecture: 历史讲座> 两个模型数据对象(对象实例),通过for循环将数据展示到前端模板
手机号、邮箱和办公地点,则是通过与 Teacher 模型 一对一关联的 TeacherInfo 模型 查询获得的
|