1

Assume, we have model

class BaseModel(models.Model):
    is_a = models.BooleanField()

and two models related to this one:

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')

What I need is to refer to A or B depending on is_a property.

For example,

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

or

if base.is_a:
    queryset = base.a.all()
else:
    queryset = base.b.all()
return queryset

i.e., every time I have to check the is_a property.

Is there more graceful way?

There are two only related models, A and B, no other ones will appear in the nearest future.

Part of the problem can be solved with django-polymorphic, e.g.:

class A(PolymorphicModel):
    ...

class B(A):
    ...

This allows to retrieve all A's and B's with one request like base.b.all(), but the problem here is that every B creates instance of A, which is unwanted.

I've considered GenericForeignKey as well. As far as I understood it has a number of limitations like "1) You can't use GenericForeignKey in query filters ; 2) a GenericForeignKey won't appear in a ModelForm" (from GenericForeignKey or ForeignKey).

OlegТ
  • 173
  • 9

1 Answers1

1

One idea is to add choices to the BaseModel to have a string representation of your boolean value. If you set the strings equal to the A and B model names, you can use the model.get_foo_display() method to return the name of the model. Then use the Python getattr() method to access attributes as variables.

class BaseModel(models.Model):

    base_model_choices = (
        (True, 'A'),
        (False, 'B'),
    )

    is_a = models.BooleanField(choices=base_model_choices)

For example,

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)
yagus
  • 1,417
  • 6
  • 14