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
模型用于记录教师信息
# 老师模型--需要继承django自带的模型基类
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) # 手机号 唯一 最大长度11
email = models.EmailField() # 邮箱
officeAddress = models.CharField(max_length=256) # 办公地址
老师的数据信息是一对一,学生的数据信息也可以是一对一
from django.db import models
# 学生模型--需要继承django自带的模型基类
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) # 手机号 唯一 最大长度11
email = models.EmailField() # 邮箱
在 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
# 学生模型--继承SchoolMember抽象基类
class Student(SchoolMember):
# 班级
grade = models.CharField(max_length=256)
# 覆盖对象对外的字符串表现形式
def __str__(self):
return self.name
# 老师模型--继承SchoolMember抽象基类
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) # 手机号 唯一 最大长度11
email = models.EmailField() # 邮箱
# 声明是抽象基类
class Meta:
abstract = True
# 学生信息模型--继承SchoolMemberInfo抽象基类
class StudentInfo(SchoolMemberInfo):
# 如果删除了学生表中的学生,关联的学生信息也会被删除
student = models.OneToOneField(Student,on_delete=models.CASCADE)
# 讲师信息模型--继承SchoolMember抽象基类
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/' ,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对象(一个对象实例列表):
,里面有
两个模型数据对象(对象实例),通过for循环将数据展示到前端模板
手机号、邮箱和办公地点,则是通过与 Teacher
模型 一对一关联的 TeacherInfo
模型 查询获得的