Django之model外键
外键和表关系
外键:
在MySQL
中,表有两种引擎,一种是InnoDB
,另外一种是myisam
。如果使用的是InnoDB
引擎,是支持外键约束的。外键的存在使得ORM
框架在处理表关系的时候异常的强大。因此这里我们首先来介绍下外键在Django
中的使用。
类定义为class ForeignKey(to,on_delete,**options)
。第一个参数是引用的是哪个模型,第二个参数是在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有CASCADE
、SET_NULL
等。
这里以一个实际案例来说明。比如有一个User
和一个Article
两个模型。一个User
可以发表多篇文章,一个Article
只能有一个Author
,并且通过外键进行引用。那么相关的示例代码如下:
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100) class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField() author = models.ForeignKey("User",on_delete=models.CASCADE)
以上使用ForeignKey
来定义模型之间的关系。即在article
的实例中可以通过author
属性来操作对应的User
模型。这样使用起来非常的方便。示例代码如下:
article = Article(title='abc',content='123')
author = User(username='张三',password='111111')
article.author = author
article.save() # 修改article.author上的值
article.author.username = '李四'
article.save()
为什么使用了ForeignKey
后,就能通过author
访问到对应的user
对象呢。因此在底层,Django
为Article
表添加了一个属性名_id
的字段(比如author的字段名称是author_id),这个字段是一个外键,记录着对应的作者的主键。以后通过article.author
访问的时候,实际上是先通过author_id
找到对应的数据,然后再提取User
表中的这条数据,形成一个模型。
如果想要引用另外一个app
的模型,那么应该在传递to
参数的时候,使用app.model_name
进行指定。以上例为例,如果User
和Article
不是在同一个app
中,那么在引用的时候的示例代码如下
# User模型在user这个app中
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100) # Article模型在article这个app中
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField() author = models.ForeignKey("user.User",on_delete=models.CASCADE)
如果模型的外键引用的是本身自己这个模型,那么to
参数可以为'self'
,或者是这个模型的名字。在论坛开发中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身。示例代码如下:
class Comment(models.Model):
content = models.TextField()
origin_comment = models.ForeignKey('self',on_delete=models.CASCADE,null=True)
# 或者
# origin_comment = models.ForeignKey('Comment',on_delete=models.CASCADE,null=True)
外键删除操作:
如果一个模型使用了外键。那么在对方那个模型被删掉后,该进行什么样的操作。可以通过on_delete
来指定。可以指定的类型如下:
CASCADE
:级联操作。如果外键对应的那条数据被删除了,那么这条数据也会被删除。PROTECT
:受保护。即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。SET_NULL
:设置为空。如果外键的那条数据被删除了,那么在本条数据上就将这个字段设置为空。如果设置这个选项,前提是要指定这个字段可以为空。SET_DEFAULT
:设置默认值。如果外键的那条数据被删除了,那么本条数据上就将这个字段设置为默认值。如果设置这个选项,前提是要指定这个字段一个默认值。SET()
:如果外键的那条数据被删除了。那么将会获取SET
函数中的值来作为这个外键的值。SET
函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。DO_NOTHING
:不采取任何行为。一切全看数据库级别的约束。
以上这些选项只是Django级别的,数据级别依旧是RESTRICT!
表关系:
表之间的关系都是通过外键来进行关联的。而表之间的关系,无非就是三种关系:一对一、一对多(多对一)、多对多等。以下将讨论一下三种关系的应用场景及其实现方式。
一对多:
- 应用场景:比如文章和作者之间的关系。一个文章只能由一个作者编写,但是一个作者可以写多篇文章。文章和作者之间的关系就是典型的多对一的关系。
实现方式:一对多或者多对一,都是通过
ForeignKey
来实现的。还是以文章和作者的案例进行讲解。
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100) class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
那么以后在给Article
对象指定author
,就可以使用以下代码来完成:
article = Article(title='abc',content='123')
author = User(username='zhiliao',password='111111')
# 要先保存到数据库中
author.save()
article.author = author
article.save()
并且以后如果想要获取某个用户下所有的文章,可以通过article_set
来实现。示例代码如下:
user = User.objects.first()
# 获取第一个用户写的所有文章
articles = user.article_set.all()
for article in articles:
print(article)
一对一:
应用场景:比如一个用户表和一个用户信息表。在实际网站中,可能需要保存用户的许多信息,但是有些信息是不经常用的。 如果把所有信息都存放到一张表中可能会影响查询效率,因此可以把用户的一些不常用的信息存放到另外一张表中我们叫做
UserExtension
。但是用户表User
和用户信息表UserExtension
就是典型的一对一了。实现方式:
Django
为一对一提供了一个专门的Field
叫做OneToOneField
来实现一对一操作。示例代码如下:
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100) class UserExtension(models.Model):
birthday = models.DateTimeField(null=True)
school = models.CharField(blank=True,max_length=50)
user = models.OneToOneField("User", on_delete=models.CASCADE)
在UserExtension
模型上增加了一个一对一的关系映射。其实底层是在UserExtension
这个表上增加了一个user_id
,来和user
表进行关联,并且这个外键数据在表中必须是唯一的,来保证一对一。
多对多:
应用场景:比如文章和标签的关系。一篇文章可以有多个标签,一个标签可以被多个文章所引用。因此标签和文章的关系是典型的多对多的关系。
实现方式:
Django
为这种多对多的实现提供了专门的Field
。叫做ManyToManyField
。还是拿文章和标签为例进行讲解。示例代码如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField("Tag",related_name="articles") class Tag(models.Model):
name = models.CharField(max_length=50)
在数据库层面,实际上Django
是为这种多对多的关系建立了一个中间表。这个中间表分别定义了两个外键,引用到article
和tag
两张表的主键。
related_name和related_query_name:
related_name:
还是以User
和Article
为例来进行说明。如果一个article
想要访问对应的作者,那么可以通过author
来进行访问。但是如果有一个user
对象,想要通过这个user
对象获取所有的文章,该如何做呢?这时候可以通过user.article_set
来访问,这个名字的规律是模型名字小写_set
。示例代码如下:
user = User.objects.get(name='张三')
user.article_set.all()
如果不想使用模型名字小写_set
的方式,想要使用其他的名字,那么可以在定义模型的时候指定related_name
。示例代码如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 传递related_name参数,以后在方向引用的时候使用articles进行访问
author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='articles')
以后在方向引用的时候。使用articles
可以访问到这个作者的文章模型。示例代码如下:
user = User.objects.get(name='张三')
user.articles.all()
如果不想使用反向引用,那么可以指定related_name='+'
。示例代码如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 传递related_name参数,以后在方向引用的时候使用articles进行访问
author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='+')
后将不能通过user.article_set
来访问文章模型了。
related_query_name:
在查找数据的时候,可以使用filter
进行过滤。使用filter
过滤的时候,不仅仅可以指定本模型上的某个属性要满足什么条件,还可以指定相关联的模型满足什么属性。比如现在想要获取写过标题为abc
的所有用户,那么可以这样写:
users = User.objects.filter(article__title='abc')
如果你设置了related_name
为articles
,因为反转的过滤器的名字将使用related_name
的名字,那么上例代码将改成如下:
users = User.objects.filter(articles__title='abc')
可以通过related_query_name
将查询的反转名字修改成其他的名字。比如article
。示例代码如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 传递related_name参数,以后在方向引用的时候使用articles进行访问
author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='articles',related_query_name='article')
那么在做反向过滤查找的时候就可以使用以下代码:
users = User.objects.filter(article__title='abc')
指定外键字段:
class Game(models.Model):
GAME_AREAS = ( )
GAME_VALIDS = (
(0, '无效'),
(1, '有效'),
)
appid = models.IntegerField(primary_key=True, unique=True, verbose_name="游戏标识")
name = models.CharField(max_length=32, verbose_name="游戏名称")
area = models.CharField(max_length=32, choices=GAME_AREAS, verbose_name="区域")
is_valid = models.SmallIntegerField(choices=GAME_VALIDS, verbose_name="是否有效") def __str__(self):
return self.name class Meta:
db_table = "common_game"
verbose_name = verbose_name_plural = "游戏表" class GameService(models.Model):
appid = models.ForeignKey(Game, db_column='appid', null=True, on_delete=models.SET_NULL, verbose_name='游戏') # 指定db_column字段
Django之model外键的更多相关文章
- django模型中有外键关系的表删除相关设置
0904自我总结 django模型中有外键关系的表删除相关设置 一.一对一 例如有Author.AuthorDetail两表 author = models.OneToOneField(to='Aut ...
- Django QuerySet 方法梳理 。model外键 多对多的保存
引用:https://feifeiyum.github.io/2017/03/28/python-django-queryset/ 说明 Models 层是 Django 框架中最强大的部分之一, 大 ...
- 【Django 2.2文档系列】Model 外键中的on_delete参数用法
场景 我们用Django的Model时,有时候需要关联外键.关联外键时,参数:on_delete的几个配置选项到底是干嘛的呢,你知道吗? 参数介绍 models.CASCADE 级联删除.Django ...
- Django ORM - 001 - 外键表查询主表信息
开始用Django做web开发,我想大家都会遇到同样的问题,那就是如何高效快速的查询需要的数据,MVC都很简单,但是ORM折腾起来就有些费时间,我准备好好研究下Django ORM,所以会有一个系列的 ...
- django模型中, 外键字段使用to_filed属性 指定到所关联主表的某个字段
在django项目的开发过程中,在设计模型时一开始将主键设置成了一个自定义的字段,但是在创建搜索索引时却发现必须要存在一个id的字段,并且为主键(不知道是否是项目一开始就这样配置的原因), 但此时表结 ...
- Django2.0里model外键和一对一的on_delete参数
在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错: TypeError: __init__() missing 1 r ...
- Django中ORM外键和表的关系(Django编程-4)
外键 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的.外键的存在使得ORM框架在处理表关系的时候异常的强大.因此这里我们首先来 ...
- django ORM的外键操作
外键约束示例 #models操作如下 class HostInfo(models.Model): servername = models.CharField(max_length=) serverip ...
- 「七天自制PHP框架」应用:Model外键链接
这里以行政区数据为例: 一级行政区数据范例: 二级行政区范例: 三级行政区范例: 在Model层建立三个Model class ProvinceModel extends Model{ public ...
- Django 表增加外键
1.创建临时表,并把原表的数据复制到临时表 先根据python manage syl article查看创建临时表 CREATE TABLE `article_article_temp` ( `id` ...
随机推荐
- bug记录|NON-STATIC METHOD CANNOT BE REFERENCED FROM A STATIC CONTEXT
bug记录|NON-STATIC METHOD CANNOT BE REFERENCED FROM A STATIC CONTEXT 问题:原因:静态方法无法调用自己定义的非静态方法 解决方案:1 ...
- [C#基础1/21] C#概述
Notion原笔记 1. C# 简介 1.1 C# 定义 C# 在继承 C 和 C++ 强大功能的同时去掉了一些它们的复杂特性,使其成为 C 语言家族中的一种高效强大的编程语言 1.2 C# 用途 用 ...
- Go实现常用的排序算法
一.插入排序 1.从第一个元素开始,该元素可以认为已经被排序 2.取出下一个元素,在已经排序的元素序列中从后向前扫描 3.如果该元素(已排序)大于新元素,将该元素移到下一位置 4.重复步骤3,直到找到 ...
- 从数据洞察到智能决策:合合信息&infiniflow RAG技术的实战案例分享
从数据洞察到智能决策:合合信息&infiniflow RAG技术的实战案例分享 标题取自 LLamaIndex,这个内容最早提出于今年 2 月份 LlamaIndex 官方博客.从 22 年 ...
- SQL全表扫描优化基础知识
1.模糊查询效率很低: 原因:like本身效率就比较低,应该尽量避免查询条件使用like:对于like '%...%'(全模糊)这样的条件,是无法使用索引的,全表扫描自然效率很低:另外,由于匹配算法的 ...
- C# .net 6 Log4net 安装、配置 以及相关问题处理
安装log4net 首先下载相关Nuget包 安装的时候注意项目的.net版本,我这里使用的是.net 6 在根目录下面新建一个配置文件 log4net.config,并且添加下面相关配置项目 < ...
- Angular 学习笔记 (Typescript 高级篇)
由于 typescript 越来越复杂. 所以特意开多一个篇幅来记入一些比较难的, 和一些到了一定程度需要知道的基础. 主要参考 https://basarat.gitbook.io/typescri ...
- SpringMVC——SSM整合-异常处理器
异常处理器 出现异常的常见位置与常见诱因: 框架内部抛出的异常:因使用不合规导致 数据层抛出异常:因外部服务器故障导致(例如:服务器访问超时) 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务 ...
- Scala编程语言基本常识
本文参考来源: Scala编程语言基本常识 – 萌面人博客 一 基本常识 Scala被称为大数据的黄金语言,它的发明人是马丁奥德斯基(Martin Odersky) 2001年诞生,融合了Ja ...
- /proc/buddyinfo
在应用程序设计过程中,内存是很重要的资源,而计算机主机的内存资源时有限的.一般而言我们可以申请到的内存是有限的,并不是想申请多大就有多大就可以申请多大的./proc/buddyinfo文件里,就记录着 ...