Наследование модели Django и внешние ключи

В принципе, у меня есть модель, где я создал суперкласс, который разделяют многие другие классы, а затем каждый из этих классов имеет некоторые уникальные функции, которые отличаются друг от друга. Пусть говорят, что класс A является суперклассом, а классы B, C и D являются дочерними элементами этого класса.

Оба класса B и класс C могут иметь кратность класса D, однако я видел, что лучше всего установить отношение внешнего ключа в классе D, которое затем относится к его родительскому классу. Теперь на других языках я могу просто сказать, что у него есть отношение ForeignKey к классу A, а затем язык распознает истинный тип классов. Однако я не думаю, что это работает с Python.

Какой лучший рекомендуемый способ решения этой проблемы?

EDIT: Вот примерно то, что я имею в виду...

class A(models.Model):
    field = models.TextField()

class B(A):
    other = <class specific functionality>

class C(A):
    other2 = <different functionality>

class D(A):
    #I would like class D to have a foreign key to either B or C, but not both.

По существу, класс B и класс C имеют несколько классов D. Но конкретный класс D принадлежит только одному из них.

Ответ 1

Из Django Docs:

Например, если вы строили базы данных "мест", вы бы создали довольно стандартный материал, такой как адрес, номер телефона и т.д. в базе данных. Затем, если вы хотите создать база данных ресторанов на вершине места, а не повторять себя и тиражирование этих полей в Модель ресторана, вы можете сделать В ресторане есть OneToOneField для Место (потому что ресторан "является" место; на самом деле, чтобы справиться с этим, вы бы обычно используют наследование, которое подразумевает неявное взаимно однозначное отношение).

Обычно вы должны иметь Restaurant наследовать от Place. К сожалению, вам нужно, что я считаю взломом: создание взаимно однозначной ссылки из подкласса в суперкласс (Restaurant to Place)

Ответ 2

Вы также можете сделать общее отношение http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#id1 и проверить типы, чтобы ограничить его B или C при настройке или сохранении. Это, вероятно, больше работы, чем выяснение прямой ссылки, но может быть чище.

Ответ 3

Я вижу здесь проблему:

class D(A):
    #D has foreign key to either B or C, but not both.

Невозможно это сделать. Вам придется добавить оба, потому что в столбцах SQL необходимо точно определить.

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

Так работает FK в Django

class A(models.Model):
    a = models.CharField(max_length=5)

class B(models.Model):
    a = model.ForeignKey(A, related_name='A')
    b = models.CharField(max_length=5)

class D(models.Model):
    a = model.ForeignKey(A, related_name='A')
    parent = model.ForeignKey(B, related_name='D')

таким образом вы можете эффективно иметь кратные D в B.

Наследование в моделях (например, класс B (A)) не работает, как я ожидал бы. Может быть, кто-то еще может объяснить это лучше.

Посмотрите этот документ. Это о взаимоотношениях "много-к-одному" в джанго.

b = B()
b.D_set.create(...)

Ответ 4

Один из способов сделать это - добавить промежуточный класс следующим образом:

class A(Model):
    class Meta(Model.Meta):
        abstract = True
    # common definitions here

class Target(A):
    # this is the target for links from D - you then need to access the 
    # subclass through ".b" or ".c"
    # (no fields here)

class B(Target):
    # additional fields here

class C(Target):
    # additional fields here        

class D(A):
    b_or_c = ForeignKey(Target)
    def resolve_target(self):
        # this does the work for you in testing for whether it is linked 
        # to a b or c instance
        try:
            return self.b_or_c.b
        except B.DoesNotExist:
            return self.b_or_c.c

Использование промежуточного класса (Target) гарантирует, что будет только одна ссылка из D на B или C. Имеет ли это смысл? Подробнее см. http://docs.djangoproject.com/en/1.2/topics/db/models/#model-inheritance.

В вашей базе данных будут таблицы для целей, B, C и D, но не A, потому что это было отмечено как абстрактное (вместо этого столбцы, связанные с атрибутами на A, будут присутствовать в Target и D).

[Warning: Я действительно не пробовал этот код - любые исправления приветствуются!]