基于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中使用第三方库xlrd来写入Excel文件示例
Apr 05 Python
python通过elixir包操作mysql数据库实例代码
Jan 31 Python
python脚本实现验证码识别
Jun 07 Python
python 字典中文key处理,读取,比较方法
Jul 06 Python
centos6.8安装python3.7无法import _ssl的解决方法
Sep 17 Python
解决vscode python print 输出窗口中文乱码的问题
Dec 03 Python
python实现QQ空间自动点赞功能
Apr 09 Python
python写入文件自动换行问题的方法
Jul 05 Python
Python3环境安装Scrapy爬虫框架过程及常见错误
Jul 12 Python
python3的pip路径在哪
Jun 23 Python
python爬虫构建代理ip池抓取数据库的示例代码
Sep 22 Python
pandas按照列的值排序(某一列或者多列)
Dec 13 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中常量,变量的作用域和生存周期
2013/08/10 PHP
php中有关字符串的4个函数substr、strrchr、strstr、ereg介绍和使用例子
2014/04/24 PHP
深入理解PHP中的global
2014/08/19 PHP
php开发中的页面跳转方法总结
2015/04/26 PHP
PHP chop()函数讲解
2019/02/11 PHP
PJ Blog修改-禁止复制的代码和方法
2006/10/25 Javascript
修改jQuery.Autocomplete插件 支持中文输入法 避免TAB、ENTER键失效、导致表单提交
2009/10/11 Javascript
关于jquery append() html时的小问题的解决方法
2010/12/16 Javascript
JS 两日期相减,获得天数的小例子(兼容IE,FF)
2013/07/01 Javascript
js获取事件源及触发该事件的对象
2013/10/24 Javascript
javascript操作html控件实例(javascript添加html)
2013/12/02 Javascript
javascript制作loading动画效果 loading效果
2014/01/14 Javascript
打造个性化的功能强大的Jquery虚拟键盘(VirtualKeyboard)
2014/10/11 Javascript
基于jQuery1.9版本如何判断浏览器版本类型
2016/01/12 Javascript
BootStrap入门教程(一)之可视化布局
2016/09/19 Javascript
js仿支付宝多方框输入支付密码效果
2016/09/27 Javascript
js实现把图片的绝对路径转为base64字符串、blob对象再上传
2016/12/29 Javascript
JavaScript中利用构造器函数模拟类的方法
2017/02/16 Javascript
jquery tmpl模板(实例讲解)
2017/09/02 jQuery
微信小程序自动客服功能
2017/11/02 Javascript
微信小程序实现省市区三级地址选择
2020/06/21 Javascript
ES6知识点整理之函数对象参数默认值及其解构应用示例
2019/04/17 Javascript
js+h5 canvas实现图片验证码
2020/10/11 Javascript
JavaScript实现网页tab栏效果制作
2020/11/20 Javascript
[01:00]选手抵达华西村 整装待发备战2016国际邀请赛中国区预选赛
2016/06/25 DOTA
Python实现简单的可逆加密程序实例
2015/03/05 Python
Anaconda2 5.2.0安装使用图文教程
2018/09/19 Python
python 获取页面表格数据存放到csv中的方法
2018/12/26 Python
使用Django搭建一个基金模拟交易系统教程
2019/11/18 Python
利用PyQt5+Matplotlib 绘制静态/动态图的实现代码
2020/07/13 Python
python实现单机五子棋
2020/08/28 Python
HTML5实现经典坦克大战坦克乱走还能发出一个子弹
2013/09/02 HTML / CSS
高中自我鉴定
2013/12/20 职场文书
心理健康心得体会
2014/01/02 职场文书
2015年董事长秘书工作总结
2015/07/23 职场文书
基于Redis延迟队列的实现代码
2021/05/13 Redis