一、多对一(ForeignKey)

多对一的关系,通常被称为外键。外键字段类的定义如下:

class ForeignKey(to, on_delete, **options)[source]

外键需要两个位置参数,一个是关联的模型,另一个是on_delete选项。实际上,在目前版本中,on_delete选项也可以不设置,但Django极力反对如此,因此在Django2.0版本后,该选项会设置为必填。

外键要定义在‘多’的一方!

from django.db import models

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

上面的例子中,每辆车都会有一个生产工厂,一个工厂可以生产N辆车,于是用一个外键字段manufacturer表示,并放在Car模型中。注意,此manufacturer非彼Manufacturer模型类,它是一个字段的名称。在Django的模型定义中,经常出现类似的英文单词大小写不同,一定要注意区分!

如果要关联的对象在另外一个app中,可以显式的指出。下例假设Manufacturer模型存在于production这个app中,则Car模型的定义如下:

class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer', # 关键在这里!!
on_delete=models.CASCADE,
)

如果要创建一个递归的外键,也就是自己关联自己的的外键,使用下面的方法:

models.ForeignKey('self', on_delete=models.CASCADE)

核心在于‘self’这个引用。什么时候需要自己引用自己的外键呢?典型的例子就是评论系统!一条评论可以被很多人继续评论,如下所示:

class Comment(models.Model):
title = models.CharField(max_length=128)
text = models.TextField()
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE)
# .....

注意上面的外键字段定义的是父评论,而不是子评论。为什么呢?因为外键要放在‘多’的一方!

在实际的数据库后台,Django会为每一个外键添加_id后缀,并以此创建数据表里的一列。在上面的工厂与车的例子中,Car模型对应的数据表中,会有一列叫做manufacturer_id。但实际上,在Django代码中你不需要使用这个列名,除非你书写原生的SQL语句,一般我们都直接使用字段名manufacturer

关系字段的定义还有个小坑。在后面我们会讲到的verbose_name参数用于设置字段的别名。很多情况下,为了方便,我们都会设置这么个值,并且作为字段的第一位置参数。但是对于关系字段,其第一位置参数永远是关系对象,不能是verbose_name,一定要注意!

参数说明:

外键还有一些重要的参数,说明如下:

on_delete

当一个被外键关联的对象被删除时,Django将模仿on_delete参数定义的SQL约束执行相应操作。比如,你有一个可为空的外键,并且你想让它在关联的对象被删除时,自动设为null,可以如下定义:

user = models.ForeignKey(
User,
models.SET_NULL,
blank=True,
null=True,
)

该参数可选的值都内置在django.db.models中,包括:

  • CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作!)
  • PROTECT:阻止上面的删除操作,但是弹出ProtectedError异常
  • SET_NULL:将外键字段设为null,只有当字段设置了null=True时,方可使用该值。
  • SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用。
  • DO_NOTHING:什么也不做。
  • SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写。
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0] class MyModel(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET(get_sentinel_user),
)

limit_choices_to

该参数用于限制外键所能关联的对象,只能用于Django的ModelForm(Django的表单模块)和admin后台,对其它场合无限制功能。其值可以是一个字典、Q对象或者一个返回字典或Q对象的函数调用,如下例所示:

staff_member = models.ForeignKey(
User,
on_delete=models.CASCADE,
limit_choices_to={'is_staff': True},
)

这样定义,则ModelForm的staff_member字段列表中,只会出现那些is_staff=True的Users对象,这一功能对于admin后台非常有用。

可以参考下面的方式,使用函数调用:

def limit_pub_date_choices():
return {'pub_date__lte': datetime.date.utcnow()} # ...
limit_choices_to = limit_pub_date_choices
# ...

related_name

用于关联对象反向引用模型的名称。以前面车和工厂的例子解释,就是从工厂反向关联到车的关系名称。

通常情况下,这个参数我们可以不设置,Django会默认以模型的小写作为反向关联名,比如对于工厂就是car,如果你觉得car还不够直观,可以如下定义:

class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer',
on_delete=models.CASCADE,
related_name='car_producted_by_this_manufacturer', # 看这里!!
)

也许我定义了一个蹩脚的词,但表达的意思很清楚。以后从工厂对象反向关联到它所生产的汽车,就可以使用maufacturer.car_producted_by_this_manufacturer了。

如果你不想为外键设置一个反向关联名称,可以将这个参数设置为“+”或者以“+”结尾,如下所示:

user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='+',
)

related_query_name

反向关联查询名。用于从目标模型反向过滤模型对象的名称。(过滤和查询在后续章节会介绍)

class Tag(models.Model):
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_name="tags",
related_query_name="tag", # 注意这一行
)
name = models.CharField(max_length=255) # 现在可以使用‘tag’作为查询名了
Article.objects.filter(tag__name="important")

to_field

默认情况下,外键都是关联到被关联对象的主键上(一般为id)。如果指定这个参数,可以关联到指定的字段上,但是该字段必须具有unique=True属性,也就是具有唯一属性。

db_constraint

默认情况下,这个参数被设为True,表示遵循数据库约束,这也是大多数情况下你的选择。如果设为False,那么将无法保证数据的完整性和合法性。在下面的场景中,你可能需要将它设置为False:

  • 有历史遗留的不合法数据,没办法的选择
  • 你正在分割数据表

当它为False,并且你试图访问一个不存在的关系对象时,会抛出DoesNotExist 异常。

swappable

控制迁移框架的动作,如果当前外键指向一个可交换的模型。使用场景非常稀少,通常请将该参数保持默认的True。

二、多对多(ManyToManyField)

多对多关系在数据库中也是非常常见的关系类型。比如一本书可以有好几个作者,一个作者也可以写好几本书。多对多的字段可以定义在任何的一方,请尽量定义在符合人们思维习惯的一方,但不要同时都定义。

class ManyToManyField(to, **options)[source]

多对多关系需要一个位置参数:关联的对象模型。它的用法和外键多对一基本类似。

在数据库后台,Django实际上会额外创建一张用于体现多对多关系的中间表。默认情况下,该表的名称是“多对多字段名+关联对象模型名+一个独一无二的哈希码”,例如‘author_books_9cdf4’,当然你也可以通过db_table选项,自定义表名。

参数说明:

related_name

参考外键的相同参数。

related_query_name

参考外键的相同参数。

limit_choices_to

参考外键的相同参数。但是对于使用through参数自定义中间表的多对多字段无效。

symmetrical

默认情况下,Django中的多对多关系是对称的。看下面的例子:

from django.db import models

class Person(models.Model):
friends = models.ManyToManyField("self")

Django认为,如果我是你的朋友,那么你也是我的朋友,这是一种对称关系,Django不会为Person模型添加person_set属性用于反向关联。如果你不想使用这种对称关系,可以将symmetrical设置为False,这将强制Django为反向关联添加描述符。

through

(定义中间表)

如果你想自定义多对多关系的那张额外的关联表,可以使用这个参数!参数的值为一个中间模型。

最常见的使用场景是你需要为多对多关系添加额外的数据,比如两个人建立QQ好友的时间。

通常情况下,这张表在数据库内的结构是这个样子的:

中间表的id列....模型对象的id列.....被关联对象的id列
# 各行数据

如果自定义中间表并添加时间字段,则在数据库内的表结构如下:

中间表的id列....模型对象的id列.....被关联对象的id列.....时间对象列
# 各行数据

看下面的例子:

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)

上面的代码中,通过class Membership(models.Model)定义了一个新的模型,用来保存Person和Group模型的多对多关系,并且同时增加了‘邀请人’和‘邀请原因’的字段。

through参数在某些使用场景中是必须的,至关重要,请务必掌握!

through_fields

接着上面的例子。Membership模型中包含两个关联Person的外键,Django无法确定到底使用哪个作为和Group关联的对象。所以,在这个例子中,必须显式的指定through_fields参数,用于定义关系。

through_fields参数接收一个二元元组('field1', 'field2'),field1是指向定义有多对多关系的模型的外键字段的名称,这里是Membership中的‘group’字段(注意大小写),另外一个则是指向目标模型的外键字段的名称,这里是Membership中的‘person’,而不是‘inviter’。

再通俗的说,就是through_fields参数指定从中间表模型Membership中选择哪两个字段,作为关系连接字段。

db_table

设置中间表的名称。不指定的话,则使用默认值。

db_constraint

参考外键的相同参数。

swappable

参考外键的相同参数。

ManyToManyField多对多字段不支持Django内置的validators验证功能。

null参数对ManyToManyField多对多字段无效!设置null=True毫无意义

三、一对一(OneToOneField)

一对一关系类型的定义如下:

class OneToOneField(to, on_delete, parent_link=False, **options)[source]

从概念上讲,一对一关系非常类似具有unique=True属性的外键关系,但是反向关联对象只有一个。这种关系类型多数用于当一个模型需要从别的模型扩展而来的情况。比如,Django自带auth模块的User用户表,如果你想在自己的项目里创建用户模型,又想方便的使用Django的认证功能,那么一个比较好的方案就是在你的用户模型里,使用一对一关系,添加一个与auth模块User模型的关联字段。

该关系的第一位置参数为关联的模型,其用法和前面的多对一外键一样。

如果你没有给一对一关系设置related_name参数,Django将使用当前模型的小写名作为默认值。

看下面的例子:

from django.conf import settings
from django.db import models # 两个字段都使用一对一关联到了Django内置的auth模块中的User模型
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',
)

这样下来,你的User模型将拥有下面的属性:

>>> user = User.objects.get(pk=1)
>>> hasattr(user, 'myspecialuser')
True
>>> hasattr(user, 'supervisor_of')
True

OneToOneField一对一关系拥有和多对一外键关系一样的额外可选参数,只是多了一个parent_link参数。


跨模块的模型:

有时候,我们关联的模型并不在当前模型的文件内,没关系,就像我们导入第三方库一样的从别的模块内导入进来就好,如下例所示:

from django.db import models
from geography.models import ZipCode class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)

django关系类型字段的更多相关文章

  1. 关系类型字段 -- Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  2. Django框架之第六篇(模型层)--单表查询和必知必会13条、单表查询之双下划线、Django ORM常用字段和参数、关系字段

    单表查询 补充一个知识点:在models.py建表是 create_time = models.DateField() 关键字参数: 1.auto_now:每次操作数据,都会自动刷新当前操作的时间 2 ...

  3. django:field字段类型

    字段类型(Field types) AutoField 它是一个根据 ID 自增长的 IntegerField 字段.通常,你不必直接使用该字段.如果你没在别的字段上指定主 键,Django 就会自动 ...

  4. Python - Django - ORM 自定义 char 类型字段

    用 CharField 定义的字段在数据库中存放为 verchar 类型 自定义 char 类型字段需要下面的代码: class FixedCharField(models.Field): " ...

  5. django models 类型整理 version:1.8.3

    django models 类型整理 version:1.8.3 网上百度到的最上面的一篇已经是11年的了,django变化很大,现在把1.8.3版的models类型大致整理了下贴出来 普通键部分 F ...

  6. Django之ORM字段和字段参数

    ORM介绍 ORM概念 ORM由来 ORM的优势 ORM的劣势 ORM总结 Django中的ORM Django项目使用MySQL数据库 Model 快速入门 字段 自定义字段 字段参数 Model ...

  7. django模型和字段

    一个模型(model)就是一个单独的.确定的数据的信息源,包含了数据的字段和操作方法.通常,每个模型映射为一张数据库中的表. 基本的原则如下: 每个模型在Django中的存在形式为一个Python类 ...

  8. Django ORM常用字段和参数

    常用字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为id的列. IntegerField 一个整数类型,范围 ...

  9. Django基础——ORM字段和字段参数

    ORM概念: 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象( 1. 不同的程序员写的SQL水平参差不齐 2. ...

随机推荐

  1. C# winform webbrowser如何指定内核为IE11? 输出 this.webbrowser.Version 显示版本是IE11的,但实际版本不是啊! 网上打的修改注册表HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULA

    最佳答案   1)假设你应用程序的名字为MyApplication.exe 2)运行Regedit,打开注册表,找到 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\M ...

  2. Jmeter接口自动化测试 (四)(持续构建)

    本文转载至http://www.cnblogs.com/chengtch/p/6145867.html  Jmeter是压力测试.接口测试工具,Ant是基于Java的构建工具,具有跨平台的作用,jen ...

  3. java中Integer 和String 之间的转换

    java中Integer 和String 之间的转换 将数组转换成字符串:char[] array = {'a','b','c','d','e'};String str = new String(ar ...

  4. iOS 第三方框架-MBProgressHUD

    MBProgressHUD提示框官网地址:https://github.com/jdg/MBProgressHUD 官网里已经提供了足够多的例子供我们使用,但在实现开发中,我们用到的只是其中的一小部分 ...

  5. unp第七章补充之socket tcp 产生 rst响应的情况

    socket tcp 产生 rst响应的情况(属于硬错误) 1.     syn发送到服务器主机,但是目的端口并未运行.则产生一个ECONRFUSED错误.客户端立即返回.比如telnet 192.1 ...

  6. owl.carousel

    简介 Owl Carousel 是一个强大.实用但小巧的 jQuery 幻灯片插件,它具有一下特点: 兼容所有浏览器 支持响应式 支持 CSS3 过度 支持触摸事件 支持 JSON 及自定义 JSON ...

  7. 深度学习的集成方法——Ensemble Methods for Deep Learning Neural Networks

    本文主要参考Ensemble Methods for Deep Learning Neural Networks一文. 1. 前言 神经网络具有很高的方差,不易复现出结果,而且模型的结果对初始化参数异 ...

  8. Linux基础命令---tune2fs

    tune2fs tune2fs允许系统管理员在Linux ext2.ext3或ext4文件系统上调整各种可调的文件系统参数.这些选项的当前值可以使用-l选项显示,也可以通过使用dumpe2fs (8) ...

  9. python之路----验证客户端合法性

    验证客户端链接的合法性 import os import hmac import socket secret_key = b'egg' sk = socket.socket() sk.bind(('1 ...

  10. keepalived+lvs高可用集群

    LVS+Keepalived 介绍 LVS LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国 ...