django ManyToMany Filed通过through自定义赋值

来源:互联网 发布:淘宝模特摆拍姿势大全 编辑:程序博客网 时间:2024/05/22 12:09

多对多关系中的额外字段

如果仅仅是处理像匹萨和装饰品的混合与搭配这样的简单情况,一个标准的ManyToMany字段完全可以满足你的需要。但有时候你可能需要处理用于描述两个模型之间的关系的数据。

例如,考虑一下这样的应用情况,将音乐家分类为不同的音乐组。一个人和他所属的组之间存在多对多的关系,所以我们可以使用ManyToManyField来表达这种关系。有时候我们可能需要搜集一些关于关系的一些细节,比如一个音乐家加入某音乐组的日期。

对于这样的情形,Django允许你指定一个用于管理多对多关系的中间模型。然后你就可以把额外的那些字段放在这个中间模型中。通过在ManyToMany字段中指定through参数可以指定用作中介的中间模型。对于上面提到的音乐家分组的例子,代码看起来就像这样:

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)。使用多于一个的外键将会引发验证错误。

唯一例外的情况是一个模型通过中间模型与自身产生多对多关系。这种情况下,两个外键指向同一个模型是允许的,但是它们会被看作同一个多对多关系的两个(不同的)方面。

当定义模型通过中间模型与其自身产生的多对多关系时,你必须使用参数symmetrical=False(查看模型字段参考了解更多细节)。

到这里,你已经通过中间模型设置好了一个多对多字段,也就是说,你已经准备好创建一些多对多关系了。要做到这一点,需要创建中间模型的实例:

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

>>> beatles.members.all()

[<Person: Ringo Starr>]

>>> ringo.group_set.all()

[<Group: The Beatles>]

>>> m2 = Membership.objects.create(person=paul, group=beatles,

...     date_joined=date(1960, 8, 1),

...     invite_reason= "Wanted to form a band.")

>>> beatles.members.all()

[<Person: Ringo Starr>, <Person: Paul McCartney>]

 

与普通的多对多字段不同的是,你不能使用addcreate命令,或者通过直接赋值(例如,beatles.members = […])的方式来创建关系:

 

#这个命令不会被运行

>>> beatles.members.add(john)

#这个也不会运行

>>> beatles.members.create(name="George Harrison")

#下面这个也不会运行

>>> beatles.members = [john, paul, ringo, george]

 

为什么呢?因为你需要通过模型Membership指出多对多关系的所有细节,所以不能直接创建PersonGroup之间的关系。简单地使用addcreate命令,或者通过直接赋值这些方式并不能指明这些细节。因此,在通过中间模型表达多对多关系的情形中,这些命令是被禁用的。创建这种类型的关系的唯一方式就是只能通过创建中间模型的实例来实现。

因为相同的原因,remove命令也是不允许被使用的。但是,可以使用clear( )方法来移除一个实例的所有多对多关系:

# Beatles have broken up

>>> beatles.members.clear()

 

一旦通过创建中间模型的实例建立起了多对多关系,就可以执行查询了。就像对待普通的多对多关系一样,你可以使用多对多关系关联的模型的属性来执行查询:

# 找出所有的组,这些组中含有成员名字以'Paul'开头的记录

>>> Group.objects.filter(members__name__startswith='Paul')

[<Group: The Beatles>]

使用一个中间模型时,你也可以直接查询它的属性:

# 找出196111之后加入Beatles组的所有成员

>>> Person.objects.filter(

...     group__name='The Beatles',

...     membership__date_joined__gt=date(1961,1,1))

[<Person: Ringo Starr]

 

 

原创粉丝点击