Django - Como renomear um campo de modelo usando o Sul?

209

Gostaria de alterar um nome de campos específicos em um modelo:

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

deve mudar para:

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

Qual é a maneira mais fácil de fazer isso usando o Sul?

Jonathan
fonte
7
Consulte também stackoverflow.com/questions/2862979/… para renomear um modelo em vez de um campo de modelo .
Caracol mecânico

Respostas:

230

Você pode usar a db.rename_columnfunção

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

O primeiro argumento de db.rename_columné o nome da tabela, por isso é importante lembrar como o Django cria nomes de tabelas :

O Django deriva automaticamente o nome da tabela do banco de dados do nome da sua classe de modelo e do aplicativo que a contém. O nome da tabela de banco de dados de um modelo é construído juntando o "rótulo do aplicativo" do modelo - o nome usado em manage.py startapp - ao nome da classe do modelo, com um sublinhado entre eles.

No caso de você ter um nome de modelo com várias palavras e com camelo, como ProjectItem, o nome da tabela será app_projectitem(ou seja, um sublinhado não será inserido entre projecte itemmesmo que seja com camelo).

googletorp
fonte
2
Isso funcionaria no nome \ full_name, mas não no campo de relação, certo?
11137 Jonathan
23
NOTA IMPORTANTE: se você usar isso, verifique se "app_foo" é o nome da tabela do banco de dados; por exemplo: "mainapp_profile", "name" é o nome da coluna antiga do banco de dados (não o nome do campo do modelo), por exemplo: "user_id" e "full_name" seriam o novo nome que você deseja que a coluna tenha (novamente, coluna do banco de dados e não o nome do campo). Portanto: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') Além disso, se você estiver lidando com chaves estrangeiras, deverá ter a parte "_id" ao renomear.
Gezim
3
Se você fizer isso db.rename_column manualmente, poderá ser necessário fazer uma migração de esquema falsa posteriormente para limpar as coisas de volta. Primeiro, migre a alteração renomeando as colunas. Em seguida, corrija o modelo (para ter o nome atualizado) e execute ./manage.py app de migração de esquema --auto && ./manage.py migrate app --fake).
precisa saber é o seguinte
24
Você também pode usar ./manage.py schemamigration my_app renaming_column_x --empty para criar uma migração vazio e apenas colocar o código nele
Ilian Iliev
11
--empty não ajuda muito; em vez disso, use --auto e modifique a migração criada. Dessa forma, o campo não reivindicará foi excluído para a próxima migração do models.py.
yasc
39

Aqui está o que eu faço:

  1. Faça o nome da coluna mudar no seu modelo (neste exemplo seria myapp/models.py)
  2. Corre ./manage.py schemamigration myapp renaming_column_x --auto

Nota renaming_column_xpode ser o que você quiser, é apenas uma maneira de atribuir um nome descritivo ao arquivo de migração.

Isso irá gerar um arquivo chamado myapp/migrations/000x_renaming_column_x.pyque excluirá sua coluna antiga e adicionará uma nova coluna.

Modifique o código neste arquivo para alterar o comportamento da migração para uma renomeação simples:

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')
não voltar
fonte
qual é o nome da coluna em seu comando? xou column_x?
precisa saber é o seguinte
A parte do comando a que você está se referindo é usada apenas para nomear a migração; pode ser o que você quiser. O nome da coluna precisará ser especificado no arquivo de migração quando você o editar.
donturner
2
Criar a --automigração primeiro é uma ótima dica. Evita problemas com o South ORM Freezer, que ocorrem se a migração tiver apenas métodos forwardse backwardsmétodos, mas não contiver o modelobjeto congelado .
Mwcz
Como respondo à pergunta do sul 'Como você está removendo esse campo, DEVE especificar um padrão'?
Bryce
3
O único problema que tenho com esse método é que db.rename_columnnão renomeia as restrições associadas à coluna. A migração ainda funcionará, mas você terá restrições com o nome do nome da coluna antiga. Eu tinha uma coluna com uma restrição de exclusividade, renomeei-a usando esse método, testei que a restrição de exclusividade ainda existia e recebi um erro, mas o nome da restrição em si ainda estava usando o nome da coluna antiga. Talvez um explícito db.delete_uniquee db.create_uniqueteria feito isso, mas eu decidi ir com a solução de sjh.
Louis
15

Eu não sabia sobre a coluna db.rename, parece útil, no entanto, no passado, adicionei a nova coluna como uma migração de esquema e criei uma migração de dados para mover valores para o novo campo, depois uma segunda migração de esquema para remover a coluna antiga

sjh
fonte
Eu também. O problema com a técnica schema-data-schema é que você acaba com nomes diferentes para seus campos / modelos. Às vezes, isso é um problema se você usar nomes canônicos para permitir despachantes ...
Jonathan
Eu apenas tentei isso e não funcionou, pois havia um conflito de nome. Eu tinha exatamente o mesmo FK, mas com um nome diferente. Não validou. Então, não faça isso.
Gezim
2
@ Jonathan, ele quer nomes diferentes! ... @ Pilgrim, gostaria de publicar algum código? Eu fiz isso várias vezes nesta semana, se seus modelos não validarem, o sul não criará as migrações.
Sjh
Isso funcionaria, mas provavelmente seria mais lento que o 'rename_coluna' que outras pessoas sugeriram. Eu sei que o MySQL pode fazer uma "ALTER TABLE ... renomear coluna" (ou seja o que for) rapidamente.
Rory
1
O @Rory db.rename_columnnão renomeia as restrições para você, então você precisa lidar com isso manualmente. Se você esquecer de fazê-lo, a migração funcionará, exceto que, sem o seu conhecimento, pode haver uma restrição ainda usando o nome da coluna antiga. Não está claro para mim se o problema é apenas cosmético ou se, em uma migração futura em que a restrição deve ser manipulada ou descartada para o sul, não será possível encontrá-la. De qualquer forma, fazê-lo aqui como sjh sugere é a maneira segura de fazê-lo: você pode deixar o Sul descobrir o que deve ser descoberto.
Louis
10

O Django 1.7 introduziu Migrações , agora você nem precisa instalar um pacote extra para gerenciar suas migrações.

Para renomear seu modelo, você precisa criar uma migração vazia primeiro:

$ manage.py makemigrations <app_name> --empty

Então você precisa editar o código da sua migração assim:

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

E depois disso, você precisa executar:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
fonte
5

Apenas mude o modelo e execute makemigrationsem 1.9

O Django detecta automaticamente que você excluiu e criou um único campo e pergunta:

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

Diga sim e a migração correta é criada. Magia.

Ciro Santilli adicionou uma nova foto
fonte
0
  1. Adicione southaos aplicativos instalados no arquivo de configuração do projeto.
  2. Comente o campo / tabela adicionado / modificado.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Remova o comentário do campo e escreva o modificado
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Se você estiver usando 'pycharm', poderá usar 'ctrl + shift + r' em vez de 'manage.py' e 'shift' para parâmetros.

ancho
fonte