基于Django OneToOneField和ForeignKey的区别详解


Posted in Python onMarch 30, 2020

根据Django官方文档介绍:

A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.

OneToOneField与ForeignKey加上unique=True效果基本一样,但是用OneToOneField反向关联会直接返回对象。

相反地,使用ForeignKey, 反向关联后会返回QuerySet。

例子:

from django.db import models

class Engine(models.Model):
 name = models.CharField(max_length=25)

 def __unicode__(self):
 return self.name

class Car(models.Model):
 name = models.CharField(max_length=25)
 engine = models.OneToOneField(Engine)

 def __unicode__(self):
 return self.name

class Engine2(models.Model):
 name = models.CharField(max_length=25)

 def __unicode__(self):
 return self.name

class Car2(models.Model):
 name = models.CharField(max_length=25)
 engine = models.ForeignKey(Engine2, unique=True)

 def __unicode__(self):
 return self.name

在python manage.py shell里输入:

>>> from testapp.models import Car, Engine
>>> c = Car.objects.get(name='Audi')
>>> e = Engine.objects.get(name='Diesel')
>>> e.car # OneToOneField的反向关联属性如果没有写relate_name, 则是对方类名的小写
<Car: Audi>
>>> from testapp.models import Car2, Engine2
>>> c2 = Car2.objects.get(name='Mazda')
>>> e2 = Engine2.objects.get(name='Wankel')
>>> e2.car2_set.all() # OneToOneField的反向关联属性如果没有写relate_name, 则是对方类名的小写_set
[<Car2: Mazda>]

补充知识:Django ForeignKey,ManyToManyField和OneToOneField的辨析

导引

模型(Models)是对网站所需信息种类的定义,其包含了网站存储数据中的重要字段和数据行为。一般来说,一个模型对于数据库中的一个表单。

字段(Fields)是模型的重要和唯一组成部分,他们由类别的属性值所指定。

Field分类

由官方文档Model field reference | Django Documentation定义:

Field一共分为AutoField、BinaryField、BooleanField、CharField、DateField、DecimalField、EmailField、FileField、FloatField、IntegerField、TextField、TimeField、URLField等类别,丰富的类别选项为数据库存储方式提供了完善的支持,而本文主要是针对如下三个关系型字段(Relationship fields):

关系型字段 对应关系
ForeignKey 多对一
ManyToManyField 多对多
OneToOneField 一对一

分析

ForeignKey

首先查看源码,在类的开头有如下参数:

many_to_many = False
many_to_one = True
one_to_many = False
one_to_one = False

由此可见,ForeignKey是many_to_one类型的,即“一对多”,我们引用官方文档给出的示例:

from django.db import models

class Car(models.Model):
 manufacturer = models.ForeignKey(
 'Manufacturer',
 on_delete=models.CASCADE,
 )
 # ...

class Manufacturer(models.Model):
 # ...
 pass

由此我们可以看到,Car类型中有manufacturer字段,其类型是对应Manufacturer类的ForeignKey。我们可以根据生活常识理解这种定义,由于一部汽车对应一个生产商,而一个生产商可以对应许多部汽车,所以两者具有“一对多”的关系,在此种情况我们使用ForeignKey。

对于每个ForeignKey,我们需要给出关联的模型和on_delete响应的选项,即

manufacturer = models.ForeignKey(
 'Manufacturer',
 on_delete=models.CASCADE,
 )

on_delete函数的作用是在此字段被删除的时候做出的响应,其可选项如下:

选项 功能
CASCADE 级联删除,此类选项模仿SQL语句ON DELETE CASCADE,再删除此字段信息的时候同时删除包含ForeignKey字段的目标(object)
PROTECT 通过django.db.IntegrityError中的ProtectedError来保护此字段不被删除,若进行删除操作则抛出错误
SET_NULL 将ForeignKey置为空,这只在null选项为True的时候产生作用
SET_DEFAULT 设为默认值(default value),此默认值已预先对ForeignKey设置
SET() 对ForeignKey设置对SET()函数传递的数值
DO_NOTHING 不进行任何操作。若数据库提高了引用完整性,则此种设置会抛出一个IntegrityError,除非对这一数据字段手动添加了SQL语句中的ON DELETE字段

还可以通过设置abstract属性来定义一个抽象类:

from django.db import models

class AbstractCar(models.Model):
 manufacturer = models.ForeignKey('Manufacturer', on_delete=models.CASCADE)

 class Meta:
 abstract = True

ForeignKey还有如下的参数可以选择:

参数 功能
limit_choices_to 通过一个限制对字段信息的某一可能选项进行约束,可以通过字典,函数或者查询值来设置
related_name 可以指定关联的类在本类中的名称,通过这一参数可以用两个字段名引用同一个类,通过这个名称父类可以取得子类的值,默认为字段名
related_query_name 用于filter函数过滤和values函数
to_field 关系关联的相关对象名称
db_constraint 控制在数据库中是否应该建立这一字段的约束
swappable 用于控制这一字段对于可交换类模型的行为

ManyToManyField

同样在源码中我们可以找到针对ManyToManyField的如下定义:

many_to_many = True
many_to_one = False
one_to_many = False
one_to_one = False

由此可以知道,ManyToManyField是针对“many-to-many”即多对多关系定义的,它需要知道它关联的类别。

官方文档给出的示例代码可以帮助理解:

from django.db import models

class Topping(models.Model):
 # ...
 pass

class Pizza(models.Model):
 # ...
 toppings = models.ManyToManyField(Topping)

在示例代码中,Pizza类的toppings字段由ManyToManyField与Toppings关联,我们可以由生活常识得出一片披萨上面会有很多种类的佐料,而一种佐料又可以用来制作多种披萨,两者满足“多对多”的关系。

ManyToManyField类有两个经常使用的参数:through和through_fields,通过这两个参数可以十分方便地建立中间项的关联,如示例代码所示:

from django.db import models

class Person(models.Model):
 name = models.CharField(max_length=50)

class Group(models.Model):
 name = models.CharField(max_length=128)
 members = models.ManyToManyField(
 Person,
 through='Membership',
 through_fields=('group', 'person'),
 )

class Membership(models.Model):
 group = models.ForeignKey(Group, on_delete=models.CASCADE)
 person = models.ForeignKey(Person, on_delete=models.CASCADE)
 inviter = models.ForeignKey(
 Person,
 on_delete=models.CASCADE,
 related_name="membership_invites",
 )
 invite_reason = models.CharField(max_length=64)

在Group类中有ManyToManyField类的字段members,这一字段通过through参数与membership联系起来,后者表示“成员资格”,即表示“团体”与“个人”之间关系的中间项,而“through_fields”字段即为中间项连接起来的两个类名,此处即group和person两个类。

ManyToManyField还有以下参数可以选择:

参数 功能
related_name 同ForeignKey,可以指定关联的类在本类中的名称
related_query_name 同ForeignKey,应用于filter和values函数
limit_choices_to 同ForeignKey,但如果自己定义了如“Membership”之类的中间类,则不会起到作用
symmetrical 对于迭代定义的ManyToManyField,其为这一字段建立一个单独的属性,而是设定symmetrical属性为True,若期望使用此类迭代关系,可以手动设置其为False
through 如上所示,用于设置中间项的名字,可以自己定义一个中间项,若不定义的话系统也会分配一个中间项
through_fields 通过元组来给出中间项关联的两个类名,可以查看上面的示例
db_table 可以通过这一属性来手动设定保存这一字段的数据表名称,若不设置则默认为字段的名称
db_contraint 是否在数据库中建立约束
swappable 设置是否指向一个可交换的模型

OneToOneField

源码中对OneToOneField的设置如下:

many_to_many = False
many_to_one = False
one_to_many = False
one_to_one = True

可知其是针对单对单的关系设定的字段。在概念上我们可以理解其为设置unique属性为True的一种类型,区别之处在于它“反向”的数值会返回一个目标值,这对于继承关系的表达十分有用,例如一下示例程序:

from django.conf import settings
from django.db import models

class MySpecialUser(models.Model):
 user = models.OneToOneField(
 settings.AUTH_USER_MODEL,
 on_delete=models.CASCADE,
 )
 supervisor = models.OneToOneField(
 settings.AUTH_USER_MODEL,
 on_delete=models.CASCADE,
 related_name='supervisor_of',
 )

OneToOneField既包含ForeignKey中的参数,又包含一个额外的参数parent_link,若定义了一个类,其继承了一个非抽象的类,而设置parent_link这个函数为True,则会将这个类视作继承的类的父类,而不是一个新的OneToOneField。

以上这篇基于Django OneToOneField和ForeignKey的区别详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现从ftp服务器下载文件的方法
Apr 30 Python
Python学习入门之区块链详解
Jul 25 Python
PyQt5每天必学之带有标签的复选框
Apr 19 Python
Python 爬取携程所有机票的实例代码
Jun 11 Python
详解Python用户登录接口的方法
Apr 17 Python
Python实现的插入排序,冒泡排序,快速排序,选择排序算法示例
May 04 Python
ubuntu 16.04下python版本切换的方法
Jun 14 Python
python rsync服务器之间文件夹同步脚本
Aug 29 Python
python中如何实现将数据分成训练集与测试集的方法
Sep 13 Python
python函数局部变量、全局变量、递归知识点总结
Nov 15 Python
python 利用turtle模块画出没有角的方格
Nov 23 Python
python游戏开发的五个案例分享
Mar 09 Python
django 扩展user用户字段inlines方式
Mar 30 #Python
Python3标准库之threading进程中管理并发操作方法
Mar 30 #Python
解决django xadmin主题不显示和只显示bootstrap2的问题
Mar 30 #Python
Python2 与Python3的版本区别实例分析
Mar 30 #Python
django xadmin中form_layout添加字段显示方式
Mar 30 #Python
基于virtualenv创建python虚拟环境过程图解
Mar 30 #Python
Python实现RabbitMQ6种消息模型的示例代码
Mar 30 #Python
You might like
php class中self,parent,this的区别以及实例介绍
2013/04/24 PHP
深入PHP异步执行的详解
2013/06/03 PHP
无需数据库在线投票调查php代码
2016/07/20 PHP
PHP+MySQL实现输入页码跳转到指定页面功能示例
2018/06/01 PHP
php遍历目录下文件并按修改时间排序操作示例
2019/07/12 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
javascript 拖放效果实现代码
2010/01/22 Javascript
jquery对元素拖动排序示例
2014/01/16 Javascript
在JavaScript中操作时间之setYear()方法的使用
2015/06/12 Javascript
jQuery平滑旋转幻灯片特效代码分享
2015/09/07 Javascript
jquery仅用6行代码实现滑动门效果
2015/09/07 Javascript
JS实现常见的TAB、弹出层效果(TAB标签,斑马线,遮罩层等)
2015/10/08 Javascript
jQuery实用技巧必备(下)
2015/11/03 Javascript
jqGrid表格应用之新增与删除数据附源码下载
2015/12/02 Javascript
Jquery $when done then的用法详解
2016/05/20 Javascript
js 判断附件后缀的简单实现方法
2016/10/11 Javascript
JS数组排序方法实例分析
2016/12/16 Javascript
深入探究AngularJs之$scope对象(作用域)
2017/07/20 Javascript
5分钟快速掌握JS中var、let和const的异同
2018/09/19 Javascript
vue中v-text / v-html使用实例代码详解
2019/04/02 Javascript
angular 实现同步验证器跨字段验证的方法
2019/04/11 Javascript
使用uni-app开发微信小程序的实现
2019/12/13 Javascript
python统计文本字符串里单词出现频率的方法
2015/05/26 Python
Python中使用bidict模块双向字典结构的奇技淫巧
2016/07/12 Python
python绘制立方体的方法
2018/07/02 Python
tensorflow模型保存、加载之变量重命名实例
2020/01/21 Python
python使用梯度下降算法实现一个多线性回归
2020/03/24 Python
浅谈keras中Dropout在预测过程中是否仍要起作用
2020/07/09 Python
Python基于Serializer实现字段验证及序列化
2020/11/04 Python
趣味比赛活动方案
2014/02/15 职场文书
设备收款委托书范本
2014/10/02 职场文书
无保留意见审计报告
2015/06/05 职场文书
航班延误投诉信
2015/07/02 职场文书
golang中的并发和并行
2021/05/08 Golang
详解NodeJS模块化
2021/06/15 NodeJs
Vue3.0写自定义指令的简单步骤记录
2021/06/27 Vue.js