基于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+django加载静态网页模板解析
Dec 12 Python
python 中字典嵌套列表的方法
Jul 03 Python
详解python如何在django中为用户模型添加自定义权限
Oct 15 Python
Python产生Gnuplot绘图数据的方法
Nov 09 Python
python爬取基于m3u8协议的ts文件并合并
Apr 26 Python
Python中函数参数匹配模型详解
Jun 09 Python
Python中判断子串存在的性能比较及分析总结
Jun 23 Python
python threading和multiprocessing模块基本用法实例分析
Jul 25 Python
python  ceiling divide 除法向上取整(或小数向上取整)的实例
Dec 27 Python
使用matplotlib的pyplot模块绘图的实现示例
Jul 12 Python
python 进阶学习之python装饰器小结
Sep 04 Python
Python学习之时间包使用教程详解
Mar 21 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 5.3.0 安装分析心得
2009/08/07 PHP
PHP中余数、取余的妙用
2015/06/29 PHP
php使用crypt()函数进行加密
2017/06/08 PHP
PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法
2017/11/10 PHP
jquery 插件 web2.0分格的分页脚本,可用于ajax无刷新分页
2008/12/25 Javascript
js 数组实现一个类似ruby的迭代器
2009/10/27 Javascript
跨浏览器的 mouseenter mouseleave 以及 compareDocumentPosition的使用说明
2010/05/04 Javascript
javascript页面上使用动态时间具体实现
2014/03/18 Javascript
jQuery入门介绍之基础知识
2015/01/13 Javascript
JS+CSS实现的拖动分页效果实例
2015/05/11 Javascript
JS实现很实用的对联广告代码(可自适应高度)
2015/09/18 Javascript
关于Iframe父页面与子页面之间的相互调用
2016/11/22 Javascript
Jquery与Bootstrap实现后台管理页面增删改查功能示例
2017/01/22 Javascript
VsCode新建VueJs项目的详细步骤
2017/09/23 Javascript
js字符串类型String常用操作实例总结
2019/07/05 Javascript
JavaScript 作用域实例分析
2019/10/02 Javascript
[01:30:54]《加油DOTA》 第三期
2014/08/18 DOTA
Python查找相似单词的方法
2015/03/05 Python
django中的setting最佳配置小结
2017/11/21 Python
python生成以及打开json、csv和txt文件的实例
2018/11/16 Python
djano一对一、多对多、分页实例代码
2019/08/16 Python
如何用OpenCV -python3实现视频物体追踪
2019/12/04 Python
详解pandas apply 并行处理的几种方法
2021/02/24 Python
CSS3中的Transition过度与Animation动画属性使用要点
2016/05/20 HTML / CSS
Canvas与图片压缩的示例代码
2017/11/28 HTML / CSS
英国快时尚女装购物网站:PrettyLittleThing
2018/08/15 全球购物
存储过程和函数的区别
2013/05/28 面试题
经理秘书岗位职责
2013/11/14 职场文书
酒吧副总经理岗位职责
2013/12/10 职场文书
单位领导证婚词
2014/01/14 职场文书
同志主要表现材料
2014/08/21 职场文书
2015年民主生活会发言材料
2014/12/15 职场文书
八年级英语教学计划
2015/01/23 职场文书
管理失职检讨书范文
2015/05/05 职场文书
Golang全局变量加锁的问题解决
2021/05/08 Golang
Vue vee-validate插件的简单使用
2021/06/22 Vue.js