Django - Jak zmienić nazwę pola modelu za pomocą South?

209

Chciałbym zmienić nazwę określonych pól w modelu:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

powinien zmienić się na:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

Jak najłatwiej to zrobić za pomocą południa?

Jonathan
źródło
7
Zobacz także stackoverflow.com/questions/2862979/…, aby zmienić nazwę modelu zamiast pola modelu .
Mechaniczny ślimak

Odpowiedzi:

230

Możesz użyć tej db.rename_columnfunkcji.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

Pierwszym argumentem db.rename_columnjest nazwa tabeli, dlatego ważne jest, aby pamiętać, w jaki sposób Django tworzy nazwy tabel :

Django automatycznie uzyskuje nazwę tabeli bazy danych na podstawie nazwy klasy modelu i aplikacji, która ją zawiera. Nazwę tabeli bazy danych modelu tworzy się, łącząc „etykietę aplikacji” modelu - nazwę użytą w manage.py startapp - z nazwą klasy modelu, z podkreśleniem między nimi.

W przypadku, gdy masz wielowątkową nazwę modelu wielbłądziego, taką jak ProjectItem, nazwą tabeli będzie app_projectitem(tzn. Znak podkreślenia nie zostanie wstawiony pomiędzy projecti itemnawet jeśli są wielbłądami).

googletorp
źródło
2
To działałoby na nazwie \ pełna_nazwa, ale nie na polu relacji, prawda?
Jonathan
23
WAŻNA UWAGA: jeśli zamierzasz tego użyć, upewnij się, że „app_foo” to nazwa tabeli bazy danych, więc na przykład: „mainapp_profile”, „name” to stara nazwa kolumny bazy danych (nie nazwa pola modelu), na przykład: „identyfikator_użytkownika” i „pełna_nazwa” to nowa nazwa, którą ma mieć kolumna (ponownie kolumna bazy danych, a nie nazwa pola). Więc: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') Ponadto, jeśli masz do czynienia z kluczami obcymi, powinieneś mieć w nich część „_id” podczas zmiany nazwy.
Gezim,
3
Jeśli ręcznie wykonasz tę kolumnę db.rename_column, może być konieczne wykonanie fałszywej schemamigracji w celu wyczyszczenia kopii zapasowej. Najpierw migruj zmianę, zmieniając nazwę kolumn. Następnie napraw model (aby miał zaktualizowaną nazwę), a następnie uruchom aplikację ./manage.py schemamigration --auto && ./manage.py migrate app --fake).
dr jimbob
24
Możesz również użyć ./manage.py schemamigration my_app renaming_column_x - pusty, aby utworzyć pustą migrację i po prostu umieścić w niej kod
Ilian Iliev
11
--empty niewiele pomaga, zamiast tego użyj --auto i zmodyfikuj utworzoną migrację. W ten sposób nie będzie twierdził, że pole zostało usunięte do następnej migracji models.py.
yasc
39

Oto co robię:

  1. Zmień nazwę kolumny w swoim modelu (w tym przykładzie byłoby to myapp/models.py)
  2. Biegać ./manage.py schemamigration myapp renaming_column_x --auto

Uwaga renaming_column_xmoże być dowolna, jest to tylko sposób na nadanie opisowej nazwy plikowi migracji.

Spowoduje to wygenerowanie pliku o nazwie, myapp/migrations/000x_renaming_column_x.pyktóry usunie starą kolumnę i doda nową kolumnę.

Zmodyfikuj kod w tym pliku, aby zmienić zachowanie migracji na prostą zmianę nazwy:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')
donturner
źródło
jaka jest nazwa kolumny w twoim poleceniu? xczy column_x?
andilabs
Część polecenia, do której się odwołujesz, służy tylko do nazwania migracji, może być dowolna. Nazwę kolumny należy podać w pliku migracji podczas jego edycji.
donturner
2
Tworzenie --automigracji pierwszy jest wielkim wskazówka. Pozwala to uniknąć problemów z South ORM Freezer, które występują, jeśli migracja ma tylko metody forwardsi backwardsmetody, ale nie zawiera zamrożonego modelobiektu.
mwcz
Jak odpowiedzieć na pytanie południowe „Ponieważ usuwasz to pole, MUSISZ określić wartość domyślną”?
Bryce
3
Jedynym problemem, jaki mam z tą metodą, jest to, że db.rename_columnnie zmienia ona nazw ograniczeń związanych z kolumną. Migracja będzie nadal działać, ale będziesz mieć ograniczenia nazwane na podstawie starej nazwy kolumny. Miałem kolumnę z ograniczeniem unikatowości, przemianowałem ją za pomocą tej metody, przetestowałem, czy ograniczenie unikalności nadal istnieje i otrzymałem błąd, ale sama nazwa ograniczenia wciąż używała starej nazwy kolumny. Być może wyraźne db.delete_uniquei db.create_uniquezrobiłbym to, ale zdecydowałem się na rozwiązanie sjh.
Louis
15

Nie wiedziałem o kolumnie db.rename, wydaje się to przydatne, jednak w przeszłości dodawałem nową kolumnę jako jeden schemat migracji, następnie utworzyłem migrację danych, aby przenieść wartości do nowego pola, a następnie drugi schemat, aby usunąć starą kolumnę

sjh
źródło
Ja też. Problem z techniką schema-data-schema polega na tym, że otrzymujesz różne nazwy dla swoich pól / modeli. Czasami jest to problem, jeśli używasz nazw kanonicznych, aby umożliwić dyspozytorom ...
Jonathan
Właśnie tego spróbowałem i nie zadziałało, ponieważ wystąpił konflikt nazw. Miałem dokładnie to samo FK, ale inne imię. Nie sprawdzono poprawności. Więc nie rób tego.
Gezim
2
@jonathan, chce różnych nazwisk! ... @pilgrim, chcesz napisać kod? Zrobiłem to kilka razy w tym tygodniu, jeśli twoje modele nie sprawdzą się, to południe nie utworzy migracji.
sjh
To by działało, ale prawdopodobnie byłoby wolniejsze niż „kolumna zmiany nazwy”, którą sugerowali inni ludzie. Wiem, że MySQL potrafi dość szybko „zmienić tabelę… zmienić nazwę kolumny” (lub cokolwiek to jest).
Rory
1
@Rory db.rename_columnnie zmieni nazwy ograniczeń, więc musisz sobie z tym poradzić ręcznie. Jeśli zapomnisz to zrobić, migracja będzie działać, z wyjątkiem tego, że bez wiedzy użytkownika może istnieć ograniczenie, które używa starej nazwy kolumny. Nie jest dla mnie jasne, czy kwestia ta ma jedynie charakter kosmetyczny, czy też w przypadku przyszłej migracji, w której należy zmanipulować ograniczenie lub je porzucić, Południe nie będzie w stanie go znaleźć. W każdym razie robienie tego tutaj, jak sugeruje sjh, jest bezpiecznym sposobem: możesz pozwolić Southowi dowiedzieć się, co powinien wiedzieć.
Louis
10

Django 1.7 wprowadził migracje, więc teraz nie musisz nawet instalować dodatkowego pakietu do zarządzania migracjami.

Aby zmienić nazwę modelu, musisz najpierw utworzyć pustą migrację:

$ manage.py makemigrations <app_name> --empty

Następnie musisz edytować kod migracji w następujący sposób:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

A potem musisz uruchomić:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
źródło
5

Wystarczy zmienić model i uruchomić makemigrationsw wersji 1.9

Django automatycznie wykrywa, że ​​usunąłeś i utworzyłeś pojedyncze pole, i pyta:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Powiedz tak, a odpowiednia migracja zostanie utworzona. Magia.

Ciro Santilli
źródło
0
  1. Dodaj southdo zainstalowanych aplikacji w pliku ustawień projektu.
  2. Skomentuj dodane / zmodyfikowane pole / tabelę.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Usuń komentarz z pola i zapisz zmodyfikowane
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Jeśli używasz „pycharm”, możesz użyć „ctrl + shift + r” zamiast „manage.py” i „shift” dla parametrów.

ancho
źródło