Как лучше всего смоделировать разнородные отношения «многие ко многим» в Django?

Я некоторое время искал, но не могу найти существующий вопрос для этого (хотя это может быть проблема незнания терминологии).

Я новичок в Django и пытаюсь создать дизайн, который со временем должен быть очень расширяемым, и заставить его работать с Django ORM. По сути, это ряд отношений «многие ко многим», использующих общую соединительную таблицу.

Дизайн представляет собой общую систему создания игр, в которой говорится: «Если вы соответствуете [требованиям], вы можете создать [награду], используя [стоимость] в качестве материалов». Это позволяет продавать предметы из любого количества магазинов, используя одну и ту же систему, и является достаточно универсальным, чтобы поддерживать широкий спектр механик — я видел, как это успешно использовалось в прошлом.

Django не поддерживает несколько отношений M2M, использующих одну и ту же соединительную таблицу (очевидно, поскольку у него нет возможности установить обратную связь), поэтому у меня, похоже, есть следующие варианты:

  • Пусть он создаст свои собственные соединительные таблицы, которых в итоге будет шесть или более, или
  • Используйте внешние ключи для соединительной таблицы вместо встроенной связи MTM.

Первый вариант немного запутан, так как я знаю, что в конечном итоге мне придется добавить дополнительные поля в соединительные таблицы. Второй вариант работает довольно хорошо. К сожалению, поскольку нет внешнего ключа от соединительной таблицы BACK к каждой из других таблиц, я постоянно борюсь с системой администратора, чтобы заставить ее делать то, что я хочу.

Вот затронутые модели:

class Craft(models.Model):
    name        = models.CharField(max_length=30)
    description = models.CharField(max_length=300, blank=True)
    cost        = models.ForeignKey('Container', related_name="craft_cost")
    reward      = models.ForeignKey('Container', related_name="craft_reward")
    require     = models.ForeignKey('Container', related_name="craft_require")

class ShopContent(models.Model):
    shopId      = models.ForeignKey(Shop)
    cost        = models.ForeignKey('Container', related_name="shop_cost")
    reward      = models.ForeignKey('Container', related_name="shop_reward")
    require     = models.ForeignKey('Container', related_name="shop_require")
    description = models.CharField(max_length=300)

class Container(models.Model):
    name        = models.CharField(max_length=30)

class ContainerContent(models.Model):
    containerId = models.ForeignKey(Container, verbose_name="Container")
    itemId      = models.ForeignKey(Item, verbose_name="Item")
    itemMin     = models.PositiveSmallIntegerField(verbose_name=u"min amount")
    itemMax     = models.PositiveSmallIntegerField(verbose_name=u"max amount")
    weight      = models.PositiveSmallIntegerField(null=True, blank=True)
    optionGroup = models.PositiveSmallIntegerField(null=True, blank=True,
                                                   verbose_name=u"option group")

Есть ли более простой и очевидный способ заставить это работать? Я пытаюсь разрешить встроенное редактирование информации ContainerContent из каждого связанного столбца в интерфейсе редактирования Craft.


person ThreeHams    schedule 12.04.2012    source источник


Ответы (2)


Похоже, у вас есть своего рода «Транзакция», которая имеет имя, описание и тип, а также определяет стоимость, вознаграждение и требования. Вы должны определить это как одну модель, а не несколько (ShopContent, Craft и т. д.).

class Transaction(models.Model):
    TYPE_CHOICES = (('Craft', 0),
                    ('Purchase', 1),
                   )
    name        = models.CharField(max_length=30)
    description = models.CharField(max_length=300, blank=True)
    cost        = models.ForeignKey('Container')
    reward      = models.ForeignKey('Container')
    require     = models.ForeignKey('Container')
    type        = models.IntegerField(choices = TYPE_CHOICES)

Теперь Shop и т. д. могут иметь один ManyToManyField до Transaction.

Независимо от того, используете ли вы эту конкретную модель, отношения cost, reward и require должны быть в одном месте, как указано выше, или в отношениях OneToOne с Craft, ShopContent и т. д. Как вы уже догадались, у вас не должно быть целой кучи сложные таблицы Many-To-Many through, которые действительно одинаковы.


Вы упомянули внизу своего поста, что вы

попытка разрешить встроенное редактирование ContainerContent информации из каждого связанного столбца в Craft интерфейсе редактирования.

Если вы моделируете несколько уровней отношений и используете приложение администратора, вам нужно либо применить какой-то вложенный встроенный патч или используйте какую-то схему связывания, подобную той, которую я использовал в своем недавнем вопросе, Как мне добавить ссылку со страницы администратора Django одного объекта в админку страница связанного объекта?

person agf    schedule 12.04.2012
comment
Другая версия ссылки: stackoverflow.com/questions/10115137/ - person agf; 12.04.2012
comment
Вложенная встроенная информация действительно полезна. Однако я отредактировал вопрос, чтобы немного лучше показать проблему отношений - через нее будут проходить несколько таблиц. Тем не менее, если я в конечном итоге сделаю это, это сократит количество необходимых столов на треть! - person ThreeHams; 12.04.2012
comment
@ThreeHams Хорошо, если у вас есть одни и те же отношения несколько раз, вам нужно смоделировать их только один раз. Либо обобщите до одной модели, как в моем примере, либо используйте либо явные поля OneToOne, либо неявные поля OneToOne с подклассами. - person agf; 13.04.2012
comment
Изменение транзакции на самом деле работает очень хорошо. Надеюсь, он выдержит изменения в будущем, но он убирает из дизайна две таблицы и три сложных связи. Спасибо за помощь! Что касается встроенного редактирования, то я понял, что область применения CMS, которую я планирую, довольно далеко выходит за пределы встроенного административного интерфейса. Учитывая, сколько ограничений я продолжаю сталкиваться, вероятно, лучше всего использовать существующее в качестве руководства и создавать его с нуля. - person ThreeHams; 13.04.2012

Я понюхал что-то здесь слишком сложное, но могу ошибаться. Для начала, это лучше? (ContainerContent будет выяснено позже)

class Cost(models.Model):
    name        = models.CharField(max_length=30)

class Reward(models.Model):
    name        = models.CharField(max_length=30)

class Require(models.Model):
    name        = models.CharField(max_length=30)

class Craft(models.Model):
    name        = models.CharField(max_length=30)
    description = models.CharField(max_length=300, blank=True)
    cost        = models.ForeignKey(Cost)
    reward      = models.ForeignKey(Reward)
    require     = models.ForeignKey(Require)

class Shop(models.Model):
    name        = models.CharField(max_length=30)
    crafts      = models.ManyToMany(Craft, blank=True)
person Udi    schedule 12.04.2012