基于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进阶教程之循环对象
Aug 30 Python
python算法表示概念扫盲教程
Apr 13 Python
Python tkinter模块中类继承的三种方式分析
Aug 08 Python
Python实现Dijkstra算法
Oct 17 Python
详解python中__name__的意义以及作用
Aug 07 Python
python输出决策树图形的例子
Aug 09 Python
python实现的分析并统计nginx日志数据功能示例
Dec 21 Python
Python 实现训练集、测试集随机划分
Jan 08 Python
使用Python 自动生成 Word 文档的教程
Feb 13 Python
python Canny边缘检测算法的实现
Apr 24 Python
Pytorch损失函数nn.NLLLoss2d()用法说明
Jul 07 Python
Python Pandas pandas.read_sql_query函数实例用法分析
Jun 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 UTF8 文件的签名问题
2009/10/30 PHP
超小PHP小马小结(方便查找后门的朋友)
2012/05/05 PHP
php调用方法mssql_fetch_row、mssql_fetch_array、mssql_fetch_assoc和mssql_fetch_objcect读取数据的区别
2012/08/08 PHP
php+ajax做仿百度搜索下拉自动提示框(有实例)
2012/08/21 PHP
php表单敏感字符过滤类
2014/12/08 PHP
php通过文件流方式复制文件的方法
2015/03/13 PHP
php中get_defined_constants函数用法实例分析
2015/05/12 PHP
php实现将Session写入数据库
2015/07/26 PHP
Zend Framework创建自己的动作助手详解
2016/03/05 PHP
php 多文件上传的实现实例
2016/10/23 PHP
php简单中奖算法(实例)
2017/08/15 PHP
js玩一玩WSH吧
2007/02/23 Javascript
JavaScript 学习笔记(十六) js事件
2010/02/01 Javascript
jquery通过visible来判断标签是否显示或隐藏
2014/05/08 Javascript
JavaScript严格模式禁用With语句的原因
2014/10/20 Javascript
浅谈jQuery中replace()方法
2015/05/13 Javascript
全面解析Bootstrap中nav、collapse的使用方法
2016/05/22 Javascript
js Canvas实现的日历时钟案例分享
2016/12/25 Javascript
js Canvas绘制圆形时钟教程
2017/02/06 Javascript
jQuery动态添加元素无法触发绑定事件的解决方法分析
2018/01/02 jQuery
详解如何在微信小程序开发中正确的使用vant ui组件
2018/09/13 Javascript
vue引入微信sdk 实现分享朋友圈获取地理位置功能
2019/07/04 Javascript
小程序外卖订单界面的示例代码
2019/12/30 Javascript
[01:08]DOTA2次级职业联赛 - Shield战队宣传片
2014/12/01 DOTA
Python下的常用下载安装工具pip的安装方法
2015/11/13 Python
Python的Twisted框架上手前所必须了解的异步编程思想
2016/05/25 Python
python 多线程将大文件分开下载后在合并的实例
2018/11/09 Python
浅谈Django+Gunicorn+Nginx部署之路
2019/09/11 Python
浅析CSS3 中的 transition,transform,translate之间区别和作用
2020/03/26 HTML / CSS
HTML5中新标签和常用标签详解
2014/03/07 HTML / CSS
工程业务员工作职责
2013/12/07 职场文书
信息专业大学生自我评价分享
2014/01/17 职场文书
公司接待方案
2014/03/08 职场文书
募捐倡议书怎么写
2014/05/14 职场文书
环境保护标语
2014/06/20 职场文书
Tomcat弱口令复现及利用
2022/05/06 Servers