Как автоматически выбрать связанную модель, используя значение поля
Предположим, у нас есть модель
class BaseModel(models.Model):
is_a = models.BooleanField()
и две модели, связанные с этой:
class A(models.Model):
value_1 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='a')
class B(models.Model):
value_1 = models.IntegerField()
value_2 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='b')
Мне нужно ссылаться на A или B в зависимости от свойства is_a.
Например,
base = BaseModel.objects.get(id=1)
if base.is_a:
obj = A.objects.create(value_1=1, base=base)
else:
obj = B.objects.create(value_1=1, value_2=2, base=base)
return obj
или
if base.is_a:
queryset = base.a.all()
else:
queryset = base.b.all()
return queryset
т.е. каждый раз, когда мне приходится проверять свойство is_a.
Есть ли более изящный способ?
Существует две единственные родственные модели, A и B, других в ближайшее время не появится.
Частично проблему можно решить с помощью django-polymorphic, например:
class A(PolymorphicModel):
...
class B(A):
...
Это позволяет получить все A и B одним запросом, как base.b.all()
, но проблема здесь в том, что каждый B создает экземпляр A, что нежелательно.
Я тоже рассматривал GenericForeignKey. Насколько я понял, у него есть ряд ограничений типа "1) нельзя использовать GenericForeignKey в фильтрах запросов; 2) GenericForeignKey не появится в ModelForm" (из GenericForeignKey или ForeignKey).
Одна из идей - добавить варианты выбора к BaseModel, чтобы иметь строковое представление вашего булевого значения. Если вы установите строки равными именам моделей A и B, вы можете использовать метод model.get_foo_display() для возврата имени модели. Затем используйте метод Python getattr()
для доступа к атрибутам как к переменным.
class BaseModel(models.Model):
base_model_choices = (
(True, 'A'),
(False, 'B'),
)
is_a = models.BooleanField(choices=base_model_choices)
Например,
base = BaseModel.objects.get(id=1)
queryset = base.getattr(models, get_is_a_display()).all()
obj = getattr(models, get_is_a_display()).objects.create(base=base)