La base de datos de mi aplicación se completa y se mantiene sincronizada con fuentes de datos externas. Tengo un modelo abstracto del que derivan todos los modelos de mi aplicación Django 2.2, definidos de la siguiente manera:
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")
El object_idcampo no se puede establecer como único ya que cada fuente de datos que uso en mi aplicación puede tener un objeto con un object_id = 1. De ahí la necesidad de rastrear el origen del objeto, por el campo object_origin.
Desafortunadamente, el ORM de Django no admite claves externas de más de una columna.
Problema
Mientras idmantengo la clave primaria autogenerada en la base de datos ( ), me gustaría hacer que mi clave externa y las relaciones de muchos a muchos sucedan en ambos campos object_idy en object_originlugar de la clave primaria id.
Lo que he intentado
Pensé en hacer algo como esto:
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)
Pero Django se queja al respecto:
myapp.B.to_a_fk: (fields.E312) The to_field 'composed_object_origin_id' doesn't exist on the related model 'myapp.A'.
Y suena legítimo, Django exceptuó el archivo dado to_fieldpara ser un campo de base de datos. Pero no hay necesidad de agregar un nuevo campo a mi CommonModelya que composed_object_type_idestá construido con dos campos no anulables ...