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 ...
fuente

Respuestas:
Usted mencionó en su comentario en la otra respuesta que object_id no es único pero es único en combinación con object_type, entonces, ¿podría usar un
unique_togetheren la metaclase? es decirfuente
¿Tiene / puede establecer el
uniqueatributo en elobject_idcampo?Si esto no funciona, cambiaría el tipo de campo a un
uuidcampo:fuente
object_idno se puede establecer como único porque hay casos en los que no es único. En realidad, en la fuente de datos externa que me proporciona los datos que uso en mi aplicación, la clave principal se compone de dos campos:object_typeyobject_id.object_idno es único, no debe crear una clave externa. Esto podría causar errores en la base de datos y no quiere eso. Si no desea utilizar el pk en su lugar, también puede administrar la relación usted mismo en lasmodels.Modelfunciones integradas.object_typeyobject_idjuntos se garantiza que serán únicos. Peroobject_idsolo no lo es.Usted es mencionado en su pregunta como " Desafortunadamente, el ORM de Django no admite claves externas de más de una columna ".
Sí, Django no proporciona ese tipo de soporte porque Django es más confiable de lo que pensamos :)
Entonces, Django proporciona una meta opción para superar este tipo de problema y esa opción es
unique_together.Puede proporcionar conjuntos de nombres de campo que, en conjunto, deben ser únicos, en su caso ...
Puede proporcionar una lista de la lista, conjuntos de conjuntos o lista simple, conjunto simple a
unique_togetheropción declass meta:.Sí, pero Django dijo que ...
Puede agregar en
UniqueConstraintlugar deunique_togetheren el mismoclass meta:en su caso, puede escribir de la siguiente manera ...Por lo tanto, la mejor práctica es usar la
constraintsopción en lugarunique_togetherdeclass meta:.fuente
Puede hacer que el ID de origen del objeto compuesto sea un campo (
composed_object_origin_id) que se actualicesavey se use comoto_field.fuente