关联关系字段 (Relationship fields)

ForeignKey,ManyToManyField与OneToOneField分别在Model中定义多对一,多对多,一对一关系。

例如,一本书由一家出版社出版,一家出版社可以出版很多书。一本书由多个作者合写,一个作者可以写很多书。

class Author(models.Model):
name=models.CharField(max_length=20)
class Publisher(models.Model):
name=models.CharField(max_length=20)
class Book(models.Model):
name=models.CharField(max_length=20)
pub=models.ForeignKey(Publisher)
authors=models.ManyToManyField(Author)

1.关联尚未定义的Model

如果你要与某个尚未定义的 model 建立关联 ,就使用 model 的名称,而不是使用 model 对象本身。

例子中,如果Publisher与Author在Book后面定义,需要写成下面的形式:

class Book(models.Model):
name=models.CharField(max_length=20)
pub=models.ForeignKey('Publisher')
authors=models.ManyToManyField('Author')

2.Model关联自身

Model可以与自身做多对一关系

class People(models.Model):
name=models.CharField(max_length=20)
leader=models.ForeignKey('self',blank=True,null=True)

Model也可以与自身做多对多关系

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

默认情况下,这种关联关系是对称的,如果Person1是Person2的朋友,那么Person2也是Person1的朋友

p1=Person()
p1.save()
p2=Person()
p2.save()
p3=Person()
p3.save()
p1.friends.add(p2,p3)

上述情况下,要查找p3的朋友,不用p3.person_set.all(),而直接用p3.friends.all()就可以了

如果想取消这种对称关系,将symmetrical设为False

class Person2(models.Model):
friends=(models.ManyToManyField("self",symmetrical=False)

这样查询p3的朋友,就需要p3.person_set.all()了

3.反向名称related_name

反向名称,用来从被关联字段指向关联字段。

注意,在你定义 抽象 model (abstract models) 时,你必须显式指定反向名称; 只有在你这么做了之后, 某些特别语法 (some special syntax) 才能正常使用。

class Book(models.Model):
name=models.CharField(max_length=20)
pub=models.ForeignKey(Publisher,related_name='pub')
authors=models.ManyToManyField(Author,related_name='author')

这样用Publisher或者Author反向查询Book时可以用related_name了:publisher1.pub.all()或者author1.author.all()。

如果不想设置反向关系,设置related_name为'+'或者以'+'结束。

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

如果有多个ManyToManyField指向同一个Model,这样反向查询FOO_set的时候就无法弄清是哪个ManyToManyField字段了,可以禁止反向关系:

users = models.ManyToManyField(User, related_name='u+')
referents = models.ManyToManyField(User, related_name='ref+')

4.数据库表现 (Database Representation)

多对一:Django 使用ForeignKey字段名称+ "_id" 做为数据库中的列名称。在上面的例子中,BOOK model 对应的数据表中会有 一个 publisher_id 列。

你可以通过显式地指定 db_column 来改变该字段的列名称,不过,除非你想自定 义 SQL ,否则没必要更改数据库的列名称。

多对多:Django 创建一个中间表来表示ManyToManyField关系。默认情况下,中间表的名称由两个关系表名结合而成。

由于某些数据库对表名的长度有限制,所以中间表的名称会自动限制在64个字符以内,并包含一个不重复的哈希字符串。这

意味着,你可能看到类似 book_authors_9cdf4 这样的表名称。你可以使用 db_table 选项手动指定中间表名称。

但是,如果你想手动指定中间表,你可以用 through 选项来指定model 使用另外某个 model 来管理多对多关系。而这个 model 就是中间表所对应的 model :

class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

这样,就可以记录某个person何时加入group了。

要建立Person与Group的关系就不能用add,create,remove了,而是需要通过Membership进行。

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason= "Needed a new drummer.")
>>> m1.save()

clear()还是可以使用的

>>> beatles.members.clear()

当多对多关系关联自身时,中间表的ForeignKey是可以指向同一个Model的,但是它们必须被看做ManyToManyField的两边,而不是对称的,需要设置 symmetrical=False。

5.其它参数 (Arguments)

5.1 ForeignKey 接受下列这些可选参数,这些参数定义了关系是如何运行的。

ForeignKey.limit_choices_to

它是一个包含筛选条件和对应值的字典,用来在 Django 管理后台筛选 关联对象。例如,利用 Python 的 datetime 模块,过滤掉不符合筛选条件关联对象:

limit_choices_to = {'pub_date__lte': datetime.date.today}

只有 pub_date 在当前日期之前的关联对象才允许被选。

也可以使用 Q 对象来代替字典,从而实现更复杂的筛选。当limit_choices_to为Q对象时,如果把此外键字段放在ModelAdmin的raw_id_fields时是不可用的。

ForeignKey.to_field

指定当前关系与被关联对象中的哪个字段关联。默认情况下,to_field 指向被关联对象的主键。

ForeignKey.on_delete

当一个model对象的ForeignKey关联的对象被删除时,默认情况下此对象也会一起被级联删除的。

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

CASCADE:默认值,model对象会和ForeignKey关联对象一起被删除

SET_NULL:将model对象的ForeignKey字段设为null。当然需要将null设为True。

SET_DEFAULT:将model对象的ForeignKey字段设为默认值。

Protect:删除ForeignKey关联对象时会生成一个ProtectedError,这样ForeignKey关联对象就不会被删除了。

SET():将model对象的ForeignKey字段设为传递给SET()的值。

def get_sentinel_user():
return User.objects.get_or_create(username='deleted')[0] class MyModel(models.Model):
user = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))

DO_NOTHING:啥也不做。

5.2 ManyToManyField 接受下列可选参数,这些参数定义了关系是如何运行的。

ManyToManyField.limit_choices_to

和 ForeignKey.limit_choices_to 用法一样。

limit_choices_to 对于通过 through 参数指定了中介表的 ManyToManyField 不起作用。

ManyToManyField.symmetrical

只要定义递归的多对多关系时起作用。

ManyToManyField.through

手动指定中间表

ManyToManyField.db_table

指定数据库中保存多对多关系数据的表名称。如果没有提供该选项,Django 就会根据两个关系表的名称生成一个新的表名,做为中间表的名称。

6.OneToOneField

class OneToOneField(othermodel[, parent_link=False, **options])

用来定义一对一关系。笼统地讲,它与声明了 unique=True 的 ForeignKey 非常相似,不同的是使用反向关联的时候,得到的不是一个对象列表,而是一个单独的对象。

在某个 model 扩展自另一个 model 时,这个字段是非常有用的;例如: 多表继承 (Multi-tableinheritance) 就是通过在子 model 中添加一个指向父 model 的一对一关联而实现的。

必须给该字段一个参数:被关联的 model 类。工作方式和 ForeignKey 一样,连递归关联 (recursive) 和 延后关联 (lazy) 都一样。

此外,OneToOneField 接受 ForeignKey 可接受的参数,只有一个参数是 OnetoOneField 专有的:OneToOneField.parent_link

如果为 True ,并且作用于继承自某个父 model 的子 model 上(这里不能是延后继承,父 model 必须真实存在 ),那么该字段就会变成指向父类实例的引用(或者叫链接),

而不是象其他OneToOneField 那样用于扩展父类并继承父类属性。

from django.db import models, transaction, IntegrityError

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80) def __unicode__(self):
return u"%s the place" % self.name class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField() def __unicode__(self):
return u"%s the restaurant" % self.place.name class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant)
name = models.CharField(max_length=50) def __unicode__(self):
return u"%s the waiter at %s" % (self.name, self.restaurant)

使用反向关联的时候,得到的不是一个对象列表,而是一个单独的对象:

>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Place.objects.get(restaurant__place__name__startswith="Demon")
<Place: Demon Dogs the place>
>>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")

Django文档——Model中的ForeignKey,ManyToManyField与OneToOneField的更多相关文章

  1. Django文档——Model中的ForeignKey,ManyToManyField与OneToOneField 关联关系字段 (Relationship fields)

    ForeignKey,ManyToManyField与OneToOneField分别在Model中定义多对一,多对多,一对一关系. 例如,一本书由一家出版社出版,一家出版社可以出版很多书.一本书由多个 ...

  2. Django文档——Model字段类型(Field Types)

    大部分内容参考自http://wrongwaycn.github.io/django11/topics/db/models/index.html#topics-db-models ,内容是django ...

  3. Django文档——Model字段选项(Field Options)

    建立一个简易Model class Person(models.Model): GENDER_CHOICES=( (1,'Male'), (2,'Female'), ) name=models.Cha ...

  4. Django文档阅读-Day1

    Django文档阅读-Day1 Django at a glance Design your model from djano.db import models #数据库操作API位置 class R ...

  5. Django文档阅读-Day2

    Django文档阅读 - Day2 Writing your first Django app, part 1 You can tell Django is installed and which v ...

  6. Django文档阅读-Day3

    Django文档阅读-Day3 Writing your first Django app, part 3 Overview A view is a "type" of Web p ...

  7. SharePoint 2013 文档库中PPT转换PDF

    通过使用 PowerPoint Automation Services,可以从 PowerPoint 二进制文件格式 (.ppt) 和 PowerPoint Open XML 文件格式 (.pptx) ...

  8. 编辑word文档过程中输入法无法正常使用

    编辑word文档过程中输入法无法正常使用怎么办??有的朋友在使用Word 2010过程中,遇到了这样的问题.每次打开word文档,程序就自动变成英文输入法,中文输入法就退出了,特别是搜狗输入法.即使在 ...

  9. SharePoint 2010遍历文档库中所有的文件,文件夹

    转:http://hi.baidu.com/sygwin/item/f99600849d51a12b110ef3eb 创建一个可视WebPart,并拖放一个label控件到ascx文件上,用于显示结果 ...

随机推荐

  1. Balanced Lineup(最简单的线段树题目)

    Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 33389   Accepted: 15665 Case Time Limit ...

  2. How to Diagnose Audi Vehicles via Tuirel S777

    Here share how to diagnose Audi cars via Tuirel S777. 1.Well connect Tuirel S777 to your Audi car, a ...

  3. Visual studio 2012 添加 GitHub

    文件-新建项目

  4. PHP用反撇号(`,也就是键盘上ESC键下面的那个,和~在同一个上面)执行外部命令

    例如: echo `whoami`; // 导出数据库,要导入的文件夹必须要有可写权限, -u -p之后的内容必须要紧挨着写 echo `mysqldump -h localhost -u$DbUse ...

  5. poj 3250 栈应用

    #include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #d ...

  6. 晒下自己App广告平台积分墙收入,顺便点评几个广告平台

    这是我之前发在爱开发App源码论坛的文章.分享了我从2011年到现在移动广告方面的收入和一些心得. 产品类型:FC.街机模拟器类App游戏 广告平台:万普世纪 广告形式:积分墙,用户先试玩几次,再玩需 ...

  7. Unity3D项目实战笔记(10):Unity3D编译IPA的PostEvents–节约时间利器

    最近,SDK支付等接入差不多了,就从Unity3D生成IPA (企业版License), 然,需要手动执行的PostEvents竟然多大10项+, 这些我默默的承受了1周时间,每次约浪费20分钟-额外 ...

  8. 值类型与引用类型(特殊的string) Typeof和GetType() 静态和非静态使用 参数传递 相关知识

    学习大神博客链接: http://www.cnblogs.com/zhili/category/421637.html 一 值类型与引用类型 需要注意的string 是特殊类型的引用类型. 使用方法: ...

  9. 20141016--for 兔子

    Console.Write("请输入月数:"); int m =int.Parse(Console.ReadLine()); ;//成兔对数ct ;//小兔对数xt ;//幼兔对数 ...

  10. OpenGL5-纹理贴图

    代码下载 #include "CELLWinApp.hpp"#include <gl/GLU.h>#include <assert.h>#include & ...