Pycharm开发Django项目外键和表关系

外键和表关系

外键:

在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam。如果使用的是InnoDB引擎,是支持外键约束的。外键的存在使得ORM框架在处理表关系的时候异常的强大。因此这里我们首先来介绍下外键在Django中的使用。

类定义为class ForeignKey(to,on_delete,**options)。第一个参数是引用的是哪个模型,第二个参数是在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有CASCADE、SET_NULL等。这里以一个实际案例来说明。比如有一个User和一个Article两个模型。一个User可以发表多篇文章,一个Article只能有一个Author,并且通过外键进行引用。那么相关的示例代码如下:

classUser(models.Model):username = models.CharField(max_length=20)    password = models.CharField(max_length=100)classArticle(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 = authorarticle.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中classUser(models.Model):username = models.CharField(max_length=20)    password = models.CharField(max_length=100)# Article模型在article这个app中classArticle(models.Model):title = models.CharField(max_length=100)    content = models.TextField()    author = models.ForeignKey("user.User",on_delete=models.CASCADE)

如果模型的外键引用的是本身自己这个模型,那么to参数可以为'self',或者是这个模型的名字。在论坛开发中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身。示例代码如下:

classComment(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来实现的。还是以文章和作者的案例进行讲解。

classUser(models.Model):username = models.CharField(max_length=20)    password = models.CharField(max_length=100)classArticle(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 = authorarticle.save()

并且以后如果想要获取某个用户下所有的文章,可以通过article_set来实现。示例代码如下:

user = User.objects.first()# 获取第一个用户写的所有文章articles = user.article_set.all()forarticleinarticles:    print(article)

一对一:

应用场景:比如一个用户表和一个用户信息表。在实际网站中,可能需要保存用户的许多信息,但是有些信息是不经常用的。如果把所有信息都存放到一张表中可能会影响查询效率,因此可以把用户的一些不常用的信息存放到另外一张表中我们叫做UserExtension。但是用户表User和用户信息表UserExtension就是典型的一对一了。

实现方式:Django为一对一提供了一个专门的Field叫做OneToOneField来实现一对一操作。示例代码如下:

classUser(models.Model):username = models.CharField(max_length=20)    password = models.CharField(max_length=100)classUserExtension(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。还是拿文章和标签为例进行讲解。示例代码如下:

classArticle(models.Model):title = models.CharField(max_length=100)    content = models.TextField()    tags = models.ManyToManyField("Tag",related_name="articles")classTag(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。示例代码如下:

classArticle(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='+'。示例代码如下:

classArticle(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。示例代码如下:

classArticle(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')

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容