¿Cómo puedo configurar dos campos de clave principal para mis modelos en Django?

4 minutos de lectura

Avatar de usuario de Vahid Kharazi
Vahid Jarazi

Tengo un modelo como este:

class Hop(models.Model):
    migration = models.ForeignKey('Migration')
    host = models.ForeignKey(User, related_name="host_set")

¿Cómo puedo hacer que la clave principal sea la combinación de migration y host?

  • posible duplicado stackoverflow.com/questions/4871966/…

    – Paulo Bu

    28 mayo 2013 a las 20:11

avatar de usuario de karthikr
Karthikr

Actualizar Django 4.0

La documentación de Django 4.0 recomienda usar Restricción única con la opción de restricciones en lugar de únicos_juntos.

Usar Restricción única con la opción de restricciones en su lugar.

Restricción única ofrece más funciones que únicos_juntos. únicos_juntos puede quedar obsoleto en el futuro.

class Hop(models.Model):
    migration = models.ForeignKey('Migration')
    host = models.ForeignKey(User, related_name="host_set")

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['migration', 'host'], name="unique_migration_host_combination"
            )
        ]

Respuesta original

Implementaría esto de manera ligeramente diferente.

Usaría una clave principal predeterminada (campo automático) y usaría la propiedad de metaclase, unique_together

class Hop(models.Model):
    migration = models.ForeignKey('Migration')
    host = models.ForeignKey(User, related_name="host_set")

    class Meta:
        unique_together = (("migration", "host"),)

Actuaría como una columna de clave principal “sustituta”.

Si realmente desea crear una clave principal de varias columnas, investigue Esta aplicación

  • Si uso unique_together, ¿significa que no tengo que tener un campo establecido como clave principal?

    – Connor

    15 de enero de 2014 a las 19:18

  • Si no especifica un AutoField, django agregaría uno para ti. Así que en resumen, no lo haces.

    – Karthikr

    15 de enero de 2014 a las 19:23


  • ¿Sigue estando actualizado en 2018 con Django 2?

    – NaturalBornCamper

    27 de marzo de 2018 a las 1:28

  • Implementarías esto de manera diferente, pero no mencionas que lo harías porque Django no admite que la clave principal sea varios campos

    – lince de fuego

    5 de octubre de 2018 a las 8:04

  • Hubo una sugerencia de edición para Django 4.0. ¿Estaba bien o no?

    -Peter Mortensen

    7 mayo 2022 a las 12:43

Avatar de usuario de Hùng Ng Vi
Hùng Ng Vi

Actualmente, los modelos de Django solo admiten una clave principal de una sola columna. Si no especificas primary_key = True para el campo en su modelo, Django creará automáticamente una columna id como clave principal.

el atributo unique_together en la clase Meta es solo una restricción para sus datos.

avatar de usuario de pulord
pulord

Si debe usar Django en una base de datos heredada, no puede modificar esquema_db.

Hay un método alternativo (feo) para solucionar este problema:

Anular los modelos ahorrar o borrar función:

# Use a raw SQL statement to save or delete the object

class BaseModel(models.Model):

    def get_max_length_unique_key(self):
        max_len_unique_key = []
        for unique_key in self._meta.unique_together:
            if len(unique_key) > len(max_len_unique_key):
                max_len_unique_key = unique_key
        return max_len_unique_key

    def get_db_conn(self):
        db_cnn = DbManage(db_ip, db_port, DATABASES_USER, DATABASES_PASSWORD, self._meta.db_table)
        db_cnn.connect()
        return db_cnn

    def save(self, *args, **kwargs):
        self.delete()
        cnn, databasename = self.get_db_conn()
        update_tables = self._meta.db_table
        key_list = ""
        values_list = ""
        for field in self._meta.fields:
            key_list += "%s," % field.name
            values_list += "\'%s\'," % str(getattr(self, field.name))

        key_list = key_list[:len(key_list) - 1]
        values_list = values_list[:len(values_list) - 1]

        sql = "insert into %s(%s) values(%s)" % (update_tables, key_list, values_list)
        logger.info("insert new record to %s" % databasename)
        cnn.excute_sql(sql)
        cnn.close()

    def delete(self, *args, **kwargs):
        cnn = self.get_db_conn()
        update_tables = self._meta.db_table
        sql = "delete from %s where " % update_tables
        for uk in self.get_max_length_unique_key():
            sql += "%s=\'%s\' and " % (uk, getattr(self, uk))
        sql = sql[:len(sql) - 4]

        logger.info("delete record from %s" % update_tables)
        cnn.excute_sql(sql)
        cnn.close()
        pass

    class Meta:
        abstract = True

class ImageList(BaseModel):

    field1 = models.CharField(primary_key=True, max_length=30)
    field2 = models.CharField(primary_key=True, max_length=30)
    field3 = models.CharField(primary_key=True, max_length=30)
    body = models.CharField(max_length=2000, blank=True, null=True)
    updated_on = models.DateTimeField(blank=True, null=True)

    class Meta:
        managed = True
        db_table="image_list"
        unique_together = (('field1', 'field2', 'field3'),)

Dado que la última versión de Django (4.0.4 en este momento) sugiere que el único_juntos puede ser obsoleto en el futurouna solución más actualizada sería usar el constraints opción de la clase Meta junto con la UniqueConstraint clase:

class Hop(models.Model):
    migration = models.ForeignKey('Migration')
    host = models.ForeignKey(User, related_name="host_set")

    class Meta:
        constraints = [
            UniqueConstraint(fields=['migration', 'host'], name="unique_host_migration"),
        ]

¿Ha sido útil esta solución?