基于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数据类型中的“冒号“[::]——分片与步长操作示例
Jan 24 Python
对numpy中数组元素的统一赋值实例
Apr 04 Python
Python 实现「食行生鲜」签到领积分功能
Sep 26 Python
Python2和Python3之间的str处理方式导致乱码的讲解
Jan 03 Python
Python简单处理坐标排序问题示例
Jul 11 Python
在jupyter notebook中调用.ipynb文件方式
Apr 14 Python
Django生成数据库及添加用户报错解决方案
Oct 09 Python
python实现一个简单RPC框架的示例
Oct 28 Python
python 实现&quot;神经衰弱&quot;翻牌游戏
Nov 09 Python
Python包argparse模块常用方法
Jun 04 Python
 分享一个Python 遇到数据库超好用的模块
Apr 06 Python
什么是Python装饰器?如何定义和使用?
Apr 11 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多线程类及用法实例
2014/12/03 PHP
PHP中使用OpenSSL生成证书及加密解密
2017/02/05 PHP
兼容所有浏览器的js复制插件Zero使用介绍
2014/03/19 Javascript
JavaScript访问CSS属性的几种方式介绍
2014/07/21 Javascript
jquery动态加载js/css文件方法(自写小函数)
2014/10/11 Javascript
适用于javascript开发者的Processing.js入门教程
2016/02/24 Javascript
Uploadify上传文件方法
2016/03/16 Javascript
node.js实现登录注册页面
2017/04/08 Javascript
微信小程序webview组件交互,内联h5页面并网页实现微信支付实现解析
2019/08/16 Javascript
Vue中keep-alive 实现后退不刷新并保持滚动位置
2020/03/17 Javascript
js实现消灭星星(web简易版)
2020/03/24 Javascript
[00:59]DOTA2英雄背景故事——上古巨神
2020/06/28 DOTA
使用python分析git log日志示例
2014/02/27 Python
Python中使用Tkinter模块创建GUI程序实例
2015/01/14 Python
详解在Python程序中解析并修改XML内容的方法
2015/11/16 Python
举例讲解Python设计模式编程中的访问者与观察者模式
2016/01/26 Python
Python设计模式编程中解释器模式的简单程序示例分享
2016/03/02 Python
浅谈python中的变量默认是什么类型
2016/09/11 Python
python3.5+tesseract+adb实现西瓜视频或头脑王者辅助答题
2018/01/17 Python
Python可以实现栈的结构吗
2020/05/27 Python
升级keras解决load_weights()中的未定义skip_mismatch关键字问题
2020/06/12 Python
Python 如何创建一个线程池
2020/07/28 Python
美国最灵活的移动提供商:Tello
2017/07/18 全球购物
巴西手表购物网站:eclock
2019/03/19 全球购物
俄罗斯药房连锁店:ASNA
2020/06/20 全球购物
物业管理求职自荐信
2013/09/25 职场文书
信用社员工先进事迹材料
2014/02/04 职场文书
《傅雷家书》教学反思
2014/04/20 职场文书
离婚协议书怎么写
2014/09/12 职场文书
工作经验交流材料
2014/12/30 职场文书
英语教师个人总结
2015/02/09 职场文书
2015年外联部工作总结
2015/04/03 职场文书
法人代表资格证明书
2015/06/18 职场文书
小学数学教学随笔
2015/08/14 职场文书
在CSS中映射鼠标位置并实现通过鼠标移动控制页面元素效果(实例代码)
2021/04/22 HTML / CSS
Golang 1.18 多模块Multi-Module工作区模式的新特性
2022/04/11 Golang