2016-03-14 28 views
5

Potrzebuję zmienić typ pola w jednym z moich modeli Django z CharField na ForeignKey. Pola są już wypełnione danymi, więc zastanawiałem się, co jest najlepszym lub właściwym sposobem, aby to zrobić. Czy mogę po prostu zaktualizować typ pola i przeprowadzić migrację, czy też są jakieś potencjalne "gotyki", o których należy pamiętać? N.B: Używam tylko operacji zarządzania Vanilla Django (makemigrations i migrate), a nie Południowej.Zmiana typu pola modelu Django z CharField na ForeignKey

+2

Uważaj na powielone wartości, jeśli nie masz unikalnego ograniczenia na CharField? To naprawdę zależy od tego, czy wcześniej duplikujesz dane, czy po prostu chcesz przenieść je do własnej tabeli. Jeśli to drugie, to najlepsza rzecz do zrobienia naprawdę zależy od tego, co oznaczają twoje dane. Django nie może wywnioskować jakiejś magicznej konwersji między CharField i ForeignKey. Musisz sam napisać sensowną migrację. – user234461

+1

Myślę, że to jest podobne do tego, czego szukasz. http://stackoverflow.com/questions/5373906/changing-the-django-data-type-in-models-without-droping-the-table – Vibhu

Odpowiedz

25

Jest to prawdopodobnie sytuacja, w której chcesz przeprowadzić wieloetapową migrację. Moja rekomendacja dla tego będzie wyglądać następująco.

Po pierwsze, załóżmy to początkowy model wewnątrz aplikacja nazywa discography:

from django.db import models 

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

Teraz okazuje się, że chcesz użyć ForeignKey dla artysty zamiast. Jak już wspomniano, nie jest to po prostu prosty proces. Należy to zrobić w kilku krokach.

Krok 1, dodać nowe pole do ForeignKey, upewniając się, aby oznaczyć ją jako wartość null:

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) 

... i stworzyć migrację do tej zmiany.

./manage.py makemigrations discography 

Krok 2, zapełnij nowe pole. Aby to zrobić, musisz utworzyć pustą migrację.

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

Po tej pustej migracji chcesz dodać pojedynczy RunPython operację do niego, aby połączyć swoje rekordy. W tym przypadku, może to wyglądać mniej więcej tak:

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() 

Teraz, gdy dane są przesyłane do nowego pola, można rzeczywiście zrobić i zostawić wszystko tak jak jest, korzystając z nowego boiska do wszystkiego. Lub, jeśli chcesz zrobić porządek, chcesz utworzyć dwie kolejne migracje.

Podczas pierwszej migracji należy usunąć swoje oryginalne pole, artist. Podczas drugiej migracji zmień nazwę nowego pola na artist_link na artist.

Odbywa się to wieloetapowo, aby upewnić się, że Django prawidłowo rozpoznaje operacje. Można utworzyć migrację ręcznie, aby sobie z tym poradzić, ale pozostawię to, aby się dowiedzieć.

+0

Dziękujemy za szybką i kompleksową odpowiedź. To również zachęciło mnie, abym w końcu usiadł i właściwie zapoznał się z migracjami Django! – ChrisM

+0

Czy jednak nie powinno to być './manage.py makemigrations --empty ...'? Krok 2 ma "makemigration" (w liczbie pojedynczej). – ChrisM

+0

Ach, tak, dzięki za złapanie literówki! –