O banco de dados do meu aplicativo é preenchido e mantido sincronizado com fontes de dados externas. Eu tenho um modelo abstrato a partir do qual todos os modelos do meu aplicativo Django 2.2 derivam, definidos da seguinte maneira:
class CommonModel(models.Model):
# Auto-generated by Django, but included in this example for clarity.
# id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
ORIGIN_SOURCEA = '1'
ORIGIN_SOURCEB = '2'
ORIGIN_CHOICES = [
(ORIGIN_SOURCEA, 'Source A'),
(ORIGIN_SOURCEB, 'Source B'),
]
object_origin = models.IntegerField(choices=ORIGIN_CHOICES)
object_id = models.IntegerField()
class A(CommonModel):
some_stuff = models.CharField()
class B(CommonModel):
other_stuff = models.IntegerField()
to_a_fk = models.ForeignKey("myapp.A", on_delete=models.CASCADE)
class C(CommonModel):
more_stuff = models.CharField()
b_m2m = models.ManyToManyField("myapp.B")
O object_id
campo não pode ser definido como exclusivo, pois cada fonte de dados que eu uso no meu aplicativo pode ter um objeto com um object_id = 1
. Daí a necessidade de rastrear a origem do objeto, pelo campo object_origin
.
Infelizmente, o ORM do Django não suporta chaves estrangeiras de mais de uma coluna.
Problema
Enquanto mantém a chave primária gerada automaticamente no banco de dados ( id
), gostaria que minha chave estrangeira e as relações muitos-para-muitos acontecessem nos campos object_id
e object_origin
em vez da chave primária id
.
O que eu tentei
Pensei em fazer algo assim:
class CommonModel(models.Model):
# Auto-generated by Django, but included in this example for clarity.
# id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
ORIGIN_SOURCEA = '1'
ORIGIN_SOURCEB = '2'
ORIGIN_CHOICES = [
(ORIGIN_SOURCEA, 'Source A'),
(ORIGIN_SOURCEB, 'Source B'),
]
object_origin = models.IntegerField(choices=ORIGIN_CHOICES)
object_id = models.IntegerField()
def _get_composed_object_origin_id(self):
return f"{self.object_origin}:{self.object_id}"
composed_object_origin_id = property(_get_composed_object_origin_id)
class A(CommonModel):
some_stuff = models.CharField()
class B(CommonModel):
other_stuff = models.IntegerField()
to_a_fk = models.ForeignKey("myapp.A", to_field="composed_object_origin_id", on_delete=models.CASCADE)
Mas o Django reclama disso:
myapp.B.to_a_fk: (fields.E312) The to_field 'composed_object_origin_id' doesn't exist on the related model 'myapp.A'.
E parece legítimo, o Django exceto o arquivado dado to_field
como um campo de banco de dados. Mas não há necessidade de adicionar um novo campo ao meu, CommonModel
pois composed_object_type_id
é construído usando dois campos não anuláveis ...