Question Changer le type de champ du modèle Django de CharField à ForeignKey


Je dois changer le type d'un champ dans l'un de mes modèles Django à partir de CharField à ForeignKey. Les champs sont déjà renseignés avec des données, alors je me demandais quelle était la meilleure ou la meilleure façon de le faire. Puis-je simplement mettre à jour le type de champ et migrer, ou existe-t-il des «pièges» possibles? N.B: J'utilise simplement les opérations de gestion de vanilla Django (makemigrations et migrate), pas au sud.


10
2018-03-14 22:17


origine


Réponses:


C'est probablement un cas où vous souhaitez effectuer une migration en plusieurs étapes. Ma recommandation à cet égard ressemblerait à ce qui suit.

Tout d'abord, supposons qu'il s'agit de votre modèle initial, dans une application appelée discography:

from django.db import models

class Album(models.Model):
    name = models.CharField(max_length=255)
    artist = models.CharField(max_length=255)

Maintenant, vous vous rendez compte que vous voulez utiliser une ForeignKey pour l'artiste à la place. Eh bien, comme mentionné, ce n'est pas simplement un processus simple pour cela. Cela doit être fait en plusieurs étapes.

Étape 1, ajoutez un nouveau champ pour ForeignKey, en veillant à le marquer comme nul:

from django.db import models

class Album(models.Model):
    name = models.CharField(max_length=255)
    artist = models.CharField(max_length=255)
    artist_link = models.ForeignKey('Artist', null=True)

class Artist(models.Model):
    name = models.CharField(max_length=255)

... et créer une migration pour ce changement.

./manage.py makemigrations discography

Étape 2, remplissez votre nouveau champ. Pour ce faire, vous devez créer une migration vide.

./manage.py makemigrations --empty --name transfer_artists discography

Une fois que vous avez cette migration vide, vous voulez ajouter un seul RunPython opération pour lier vos dossiers. Dans ce cas, cela pourrait ressembler à ceci:

def link_artists(apps, schema_editor):
    Album = apps.get_model('discography', 'Album')
    Artist = apps.get_model('discography', 'Artist')
    for album in Album.objects.all():
        artist, created = Artist.objects.get_or_create(name=album.artist)
        album.artist_link = artist
        album.save()

Maintenant que vos données sont transférées dans le nouveau champ, vous pourriez réellement être terminé et tout laisser tel quel, en utilisant le nouveau champ pour tout. Ou, si vous souhaitez effectuer un peu de nettoyage, vous souhaitez créer deux autres migrations.

Pour votre première migration, vous souhaitez supprimer votre champ d'origine, artist. Pour votre deuxième migration, renommez le nouveau champ artist_link à artist.

Cela se fait en plusieurs étapes pour s'assurer que Django reconnaît correctement les opérations. Vous pouvez créer une migration manuellement pour gérer cela, mais je vous laisse le soin de le comprendre.


32
2018-03-14 23:37