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

mem*_*elf 1 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. 
Run Code Online (Sandbox Code Playgroud)

我想做以下(伪代码):

# 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)
Run Code Online (Sandbox Code Playgroud)

我创建了一个新Config对象,new_config并说这个配置应该有3个房子.然后我的模型应该自动检查并强制我将正确数量的房屋链接回去Config.另外,a 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')
Run Code Online (Sandbox Code Playgroud)

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

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

Gar*_*ees 5

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

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

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

class Config(model.Model):
    # ...
    houses = model.ManyToManyField('House', through = 'ConfigHouses')
Run Code Online (Sandbox Code Playgroud)

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

# 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()
Run Code Online (Sandbox Code Playgroud)

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

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 |
-------------------------------
Run Code Online (Sandbox Code Playgroud)

要查找特定配置中特定类型的房屋数量,您可以直接查询中间表,也可以通过以下任一模型查询:

>>> 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
Run Code Online (Sandbox Code Playgroud)

原始答案

(最初的问题并没有说清楚多对多关系需要多重性,但我会留下原始答案,以防它有用.)

你最初的想法(houses应该是a ManyToManyField)是正确的想法.只是为了House在定义类之前引用类,您需要传递类的名称.请参阅Django文档:

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

拥有一个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')
Run Code Online (Sandbox Code Playgroud)

你可以更新许多一对多关系使用RelatedManager接口:有方法add,create,remove,clear,或者你可以给它分配:

config.houses = House.objects.filter(color = 'red')
Run Code Online (Sandbox Code Playgroud)