使用动态数量的外键条目创建灵活的django数据模型

时间:2012-08-27 16:04:08

标签: django django-models

我在编写django数据模型时遇到问题,该模型可以将同一类型的多个对象链接回单个数据条目。以下是我目前的型号:

class House(models.Model):
    name = models.CharField(max_length=200, unique=True)
    color = models.CharField(max_length=200, unique=True) 

class Config(models.Model)
   name = models.CharField(max_length=200)
   nbr_houses = models.PositiveIntegerField()
   houses = models.ManyToManyField(House) # this does not work, since I can only map a house once back to Config. 

我想做以下(伪代码):

# define a new config and enforce that is should have houses.
$ a = Config(name = 'new_config', nbr_houses = 3)
# associate individual houses with this config
$ a.houses[0] = House(pk= 1)
# associate the same house a second time with this config
$ a.houses[1] = House(pk= 1)
# associate a third house to config.
$ a.houses[2] = House(pk= 2)

我创建了一个新的Config对象new_config,并说这个配置应该有3个房子。然后,我的模型应自动检查并强制我将正确数量的房屋链接回Config。此外,House可能会引用两次相同的配置。

我正在考虑以下列方式编写Config

class Config(models.Model)
   name = models.CharField(max_length=200)
   nbr_houses = models.PositiveIntegerField()
   houses = models.ManyToManyField(House, related_name='house0')
   houses1 = models.ManyToManyField(House, related_name='house1')
   houses2 = models.ManyToManyField(House, related_name='house2')
   houses3 = models.ManyToManyField(House, related_name='house3')

基本上反映了可能的最大关联数,但这有点不灵活。有什么更好的方法来实现这个目标?

编辑:想象一下以下用例: 您想编写一个计算机游戏,您可以在其中定义场景的复杂性。每个场景都保存为Config。每个场景,我可以放置许多房屋x。房子没有区别,它们只是模板。因此可以制作10个红色房屋,2个蓝色房屋和1个黄色房屋。因此,对于每个Config条目,我希望能够关联不同数量的房屋(对象)。也许以后也会有马:)我希望你能得到这个想法。

1 个答案:

答案 0 :(得分:5)

问题已更新,以解释您可能希望将每个房屋的某些数字与配置相关联。为了在Django中执行此操作,您需要一个新模型,Django称之为intermediate model。像这样:

class ConfigHouses(model.Model):
    config = model.ForeignKey('Config')
    house = model.ForeignKey('House')
    count = model.PositiveIntegerField()
    class Meta:
        unique_together = ('config', 'house')

然后在Config类中声明ManyToManyField并将中间模型分配给through参数:

class Config(model.Model):
    # ...
    houses = model.ManyToManyField('House', through = 'ConfigHouses')

要在配置中添加房屋,您需要创建中间模型的新实例:

# Level 1 contains 4 red houses and 5 yellow houses
c = Config.objects.get_or_create(name = 'level-1')

red = House.objects.get_or_create(name = 'red')
ch = ConfigHouses(config = c, house = red, count = 4)
ch.save()

yellow = House.objects.get_or_create(name = 'yellow')
ch = ConfigHouses(config = c, house = yellow, count = 5)
ch.save()

执行上述查询后,数据库将如下所示:

CONFIG TABLE
----------------
| pk | name    |
|----|---------|
|  1 | level-1 |
----------------

HOUSE TABLE
----------------
| pk | name    |
|----|---------|
|  1 | red     |
|  2 | yellow  |
----------------

CONFIGHOUSES TABLE
-------------------------------
| pk | config | house | count |
|----|--------|-------|-------|
|  1 |      1 |     1 |     4 |
|  2 |      1 |     2 |     5 |
-------------------------------

要了解特定配置中特定类型的房屋数量,您可以直接或通过其中一个模型查询中间表:

>>> ConfigHouses.objects.get(config = c, house__name = 'red').count
4
>>> c.confighouses.get(house__name = 'red').count
4
>>> yellow.confighouses_set.get(config = c).count
5

原始答案

(最初的问题并没有明确表示多对多关系需要多重性,但我会留下原始答案,以防它有用。)

您最初的想法(houses应该是ManyToManyField)是正确的想法。只是为了在定义它之前引用House类,您需要传递类的名称。见the Django documentation

  

如果需要在尚未定义的模型上创建关系,可以使用模型的名称,而不是模型对象本身。

拥有nbr_houses字段也是一个坏主意,因为如果有人更新多对多关系而又不记得更新nbr_houses字段,那么该字段可能会变得不正确。您可以使用houses.count()获取相关房屋的数量。

所以你的Config课应该是这样的:

class Config(models.Model)
   name = models.CharField(max_length=200)
   # The class House is not yet defined, so refer to it by name.
   houses = models.ManyToManyField('House')

您可以使用RelatedManager interface更新多对多关系:有方法addcreateremoveclear,或者你可以分配给它:

config.houses = House.objects.filter(color = 'red')