68 votes

Comment puis-je définir deux champs de clé primaire pour mes modèles dans Django ?

J'ai un modèle comme celui-ci :

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

Comment puis-je faire en sorte que la clé primaire soit la combinaison des éléments suivants migration y host ?

110voto

karthikr Points 36157

Mise à jour de Django 4.0

La documentation de Django 4.0 recommande d'utiliser UniqueConstraint avec l'option contraintes au lieu de unique_together .

Utilice UniqueConstraint avec l'option contraintes à la place.

UniqueConstraint fournit plus de fonctionnalités que unique_together . unique_together peuvent être dépréciées à l'avenir.

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

Réponse originale

Je mettrais cela en œuvre de manière légèrement différente.

J'utiliserais une clé primaire par défaut (champ auto), et j'utiliserais la propriété meta class, unique_together

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

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

Elle agirait comme une colonne de clé primaire "de substitution".

Si vous souhaitez vraiment créer une clé primaire à plusieurs colonnes, consultez le site suivant cette application

26voto

Hùng Ng Vi Points 388

Actuellement, les modèles Django ne prennent en charge qu'une clé primaire à une seule colonne. Si vous ne spécifiez pas primary_key = True pour le champ dans votre modèle, Django créera automatiquement une colonne id comme clé primaire.

L'attribut unique_together en classe Meta n'est qu'une contrainte pour vos données.

4voto

pulord Points 47

Si vous devez utiliser Django sur une base de données existante, vous ne pouvez pas modifier les paramètres suivants db_schema .

Il existe une méthode de contournement (laide) pour résoudre ce problème :

Remplacer les modèles sauver o supprimer fonction :

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

2voto

u09 Points 419

Puisque la dernière version de Django (4.0.4 à l'heure actuelle) suggère que l'unique_together peut être déprécié à l'avenir une solution plus moderne consisterait à utiliser la fonction constraints de la classe Meta avec l'option UniqueConstraint classe :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X