Então, há cerca de um ano, iniciei um projeto e, como todos os novos desenvolvedores, não me concentrei muito na estrutura, no entanto, agora que estou mais junto com o Django, começou a parecer que o layout do meu projeto, principalmente meus modelos, são horríveis na estrutura .
Eu tenho modelos mantidos principalmente em um único aplicativo e, na verdade, a maioria desses modelos deve estar em seus próprios aplicativos individuais. Tentei resolver isso e movê-los para o sul. No entanto, achei complicado e muito difícil devido às chaves estrangeiras ect.
No entanto, devido ao Django 1.7 e suporte embutido para migrações, existe uma maneira melhor de fazer isso agora?
Respostas:
Estou removendo a resposta antiga, pois pode resultar em perda de dados. Como o ozan mencionou , podemos criar duas migrações, uma em cada aplicativo. Os comentários abaixo deste post se referem à minha resposta antiga.
Primeira migração para remover o modelo do 1º aplicativo.
Edite o arquivo de migração para incluir essas operações.
Segunda migração, que depende da primeira migração e cria a nova tabela no 2º aplicativo. Depois de mover o código do modelo para o segundo aplicativo
e edite o arquivo de migração para algo assim.
fonte
./manage.py migrate
tudo terminará em bom estado. Falsificar migrações manualmente é uma maneira incorreta da IMO.Isso pode ser feito com bastante facilidade usando
migrations.SeparateDatabaseAndState
. Basicamente, usamos uma operação de banco de dados para renomear a tabela simultaneamente com duas operações de estado para remover o modelo do histórico de um aplicativo e criá-lo no outro.Remover do aplicativo antigo
Na migração:
Adicionar à nova aplicação
Primeiro, copie o modelo para o novo model.py do aplicativo, depois:
Isso gerará uma migração com uma
CreateModel
operação ingênua como a única operação. Embrulhe isso em umaSeparateDatabaseAndState
operação para que não tentemos recriar a tabela. Inclua também a migração anterior como uma dependência:fonte
Eu encontrei o mesmo problema. A resposta de Ozan me ajudou muito, mas infelizmente não foi suficiente. Na verdade, eu tinha várias ForeignKey vinculadas ao modelo que eu queria mover. Após algumas dores de cabeça, encontrei a solução e resolvi publicá-la para resolver o tempo das pessoas.
Você precisa de mais 2 etapas:
ForeignKey
ligação aTheModel
emIntegerfield
. Então corrapython manage.py makemigrations
ForeignKey(TheModel)
vez deIntegerField()
. Em seguida, faça as migrações novamente (python manage.py makemigrations
). Em seguida, você pode migrar e deve funcionar (python manage.py migrate
)Espero que ajude. Claro que testá-lo no local antes de tentar na produção para evitar surpresas ruins :)
fonte
Como eu fiz isso (testado no Django == 1.8, com postgres, provavelmente também 1.7)
Situação
app1.YourModel
mas você deseja que ele vá para: app2.YourModel
adicione isso ao app2.YourModel:
$ python manage.py makemigrations app2
Uma nova migração (por exemplo, 0009_auto_something.py) é feita no app2 com uma instrução migrations.CreateModel (), mova essa instrução para a migração inicial do app2 (por exemplo, 0001_initial.py) (será como sempre esteve lá). E agora remova a migração criada = 0009_auto_something.py
Assim como você age, como o app2.YourModel sempre esteve lá, agora remova a existência do app1.YourModel das suas migrações. Significado: comente as instruções CreateModel e todos os ajustes ou migração de dados que você usou depois disso.
E, é claro, todas as referências ao app1.YourModel precisam ser alteradas para app2.YourModel através do seu projeto. Além disso, não esqueça que todas as chaves estrangeiras possíveis para app1.YourModel nas migrações devem ser alteradas para app2.YourModel
Agora, se você migrar o $ python manage.py, nada mudou, também quando você fizer as migrações do $ python manage.py, nada de novo foi detectado.
Agora, o toque final: remova o Class Meta de app2.YourModel e faça $ python manage.py makemigrations app2 && python manage.py migrate app2 (se você olhar para essa migração, verá algo assim :)
table = None, significa que ele assumirá o nome da tabela padrão, que neste caso será app2_yourmodel.
PS durante a migração, verá que esse content_type app1.yourmodel foi removido e pode ser excluído. Você pode dizer sim a isso, mas apenas se não o usar. Caso você dependa muito dele para que os FKs desse tipo de conteúdo estejam intactos, ainda não responda sim ou não, mas vá para o banco de dados nesse momento manualmente e remova o tipo de conteúdo app2.yourmodel e renomeie o contenttype app1. yourmodel para app2.yourmodel e continue respondendo não.
fonte
app_label = 'app1'
opção meta.field1 = models.ForeignKey('app1.myModel').
Quando eu migrar, eu recebo uma ValueError afirmando quefield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Recebo migrações nervosas de codificação manual (conforme exigido pela resposta de Ozan ), de modo que o seguinte combina as estratégias de Ozan e Michael para minimizar a quantidade de codificação manual necessária:
makemigrations
.app1
paraapp2
Conforme recomendado por @Michael, apontamos o novo modelo para a tabela antiga do banco de dados usando a
db_table
opção Meta no modelo "novo":Corra
makemigrations
. Isso irá gerarCreateModel
dentroapp2
eDeleteModel
dentroapp1
. Tecnicamente, essas migrações se referem exatamente à mesma tabela e removeriam (incluindo todos os dados) e recriariam a tabela.Na realidade, não queremos (ou precisamos) fazer nada com a mesa. Nós apenas precisamos do Django acreditar que a mudança foi feita. Pela resposta de @ Ozan, a
state_operations
bandeiraSeparateDatabaseAndState
faz isso. Então, nós envolvemos todas asmigrations
entradas NOS DOIS MIGRAÇÕES FILES comSeparateDatabaseAndState(state_operations=[...])
. Por exemplo,torna-se
Você também precisa garantir que a nova
CreateModel
migração "virtual" dependa de qualquer migração que realmente tenha criado ou alterado a tabela original . Por exemplo, se suas novas migrações sãoapp2.migrations.0004_auto_<date>
(paraCreate
) eapp1.migrations.0007_auto_<date>
(paraDelete
), a coisa mais simples a fazer é:app1.migrations.0007_auto_<date>
e copie suaapp1
dependência (por exemplo('app1', '0006...'),
). Essa é a migração "imediatamente anterior"app1
e deve incluir dependências em toda a lógica de criação de modelo real.app2.migrations.0004_auto_<date>
e adicione a dependência que você acabou de copiar em suadependencies
lista.Se você tem um
ForeignKey
relacionamento com o modelo que está movendo, o que foi dito acima pode não funcionar. Isso acontece porque:ForeignKey
alteraçõesForeignKey
alteraçõesstate_operations
, precisamos garantir que elas sejam separadas das operações da tabela.NOTA: O Django 2.2 adicionou um aviso (
models.E028
) que quebra esse método. Você pode contornar isso,managed=False
mas eu não o testei.O conjunto "mínimo" de operações difere dependendo da situação, mas o procedimento a seguir deve funcionar para a maioria / todas as
ForeignKey
migrações:app1
paraapp2
, definadb_table
, mas NÃO altere nenhuma referência FK.makemigrations
e agrupe toda aapp2
migraçãostate_operations
(veja acima)app2
CreateTable
últimaapp1
migraçãomodels.py
(NÃO remova-o) para que ele não concorra com a classe importada.Execute,
makemigrations
mas NÃO envolva nadastate_operations
(as alterações do FK realmente devem acontecer). Adicione uma dependência em todas asForeignKey
migrações (ou sejaAlterField
) àCreateTable
migraçãoapp2
(você precisará desta lista para a próxima etapa, portanto, acompanhe-as). Por exemplo:CreateModel
por exemplo,app2.migrations.0002_auto_<date>
e copie o nome dessa migração.Encontre todas as migrações que tenham uma ForeignKey para esse modelo (por exemplo, pesquisando
app2.YourModel
para encontrar migrações como:Adicione a
CreateModel
migração como uma dependência:Remova os modelos de
app1
makemigrations
e agrupe aapp1
migraçãostate_operations
.ForeignKey
migrações (ou seja,AlterField
) da etapa anterior (pode incluir migrações emapp1
eapp2
).DeleteTable
já dependiam dasAlterField
migrações, então não precisei aplicá-las manualmente (ou seja,Alter
antesDelete
).Neste ponto, o Django está pronto. O novo modelo aponta para a tabela antiga e as migrações do Django convenceram-na de que tudo foi realocado adequadamente. A grande ressalva (da resposta de @ Michael) é que um novo
ContentType
é criado para o novo modelo. Se você vincular (por exemplo, porForeignKey
) a tipos de conteúdo, precisará criar uma migração para atualizar aContentType
tabela.Eu queria limpar depois de mim mesmo (opções Meta e nomes de tabelas), então usei o seguinte procedimento (do @Michael):
db_table
entrada Metamakemigrations
novamente para gerar a renomeação do banco de dadosDeleteTable
migração. Não parece que seja necessário, poisDelete
deve ser puramente lógico, mas cometer erros (por exemploapp1_yourmodel
, não existe) se não existir.fonte
table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
managed=False
mas não estou em lugar para verificar.Outra alternativa hacky, se os dados não forem grandes ou muito complicados, mas ainda importantes de manter, é:
fonte
Copiado da minha resposta em https://stackoverflow.com/a/47392970/8971048
Caso você precise mover o modelo e não tenha mais acesso ao aplicativo (ou não queira mais), crie uma nova Operação e considere criar um novo modelo apenas se o modelo migrado não existir.
Neste exemplo, estou passando 'MyModel' de old_app para myapp.
fonte
Isso é testado aproximadamente, portanto, não esqueça de fazer backup do seu banco de dados !!!
Por exemplo, existem dois aplicativos:
src_app
edst_app
queremos mover o modeloMoveMe
desrc_app
paradst_app
.Crie migrações vazias para os dois aplicativos:
Vamos supor que as novas migrações são
XXX1_src_app_new
eXXX1_dst_app_new
, as principais migrações anteriores sãoXXX0_src_app_old
eXXX0_dst_app_old
.Adicione uma operação que renomeie a tabela para o
MoveMe
modelo e renomeie seu app_label no ProjectState paraXXX1_dst_app_new
. Não se esqueça de adicionar dependência àXXX0_src_app_old
migração. AXXX1_dst_app_new
migração resultante é:Adicione dependência de
XXX1_dst_app_new
paraXXX1_src_app_new
.XXX1_src_app_new
é uma migração não operacional que é necessária para garantir que futurassrc_app
migrações sejam executadas depoisXXX1_dst_app_new
.Mover
MoveMe
desrc_app/models.py
paradst_app/models.py
. Então corra:Isso é tudo!
fonte
Você pode tentar o seguinte (não testado):
src_app
paradest_app
dest_app
; verifique se a migração do esquema depende dasrc_app
migração mais recente ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )dest_app
, que copia todos os dados desrc_app
src_app
; verifique se a migração do esquema depende da migração mais recente (dados) dedest_app
- ou seja: a migração da etapa 3Observe que você copiará a tabela inteira, em vez de movê- la, mas dessa maneira os dois aplicativos não precisam tocar em uma tabela que pertence ao outro aplicativo, o que eu acho mais importante.
fonte
Digamos que você esteja movendo o modelo TheModel de app_a para app_b.
Uma solução alternativa é alterar as migrações existentes manualmente. A ideia é que sempre que você vir uma operação alterando o TheModel nas migrações do app_a, copie essa operação para o final da migração inicial do app_b. E cada vez que você vê uma referência 'app_a.TheModel' nas migrações de app_a, você a altera para 'app_b.TheModel'.
Acabei de fazer isso em um projeto existente, onde queria extrair um determinado modelo para um aplicativo reutilizável. O procedimento correu bem. Eu acho que as coisas seriam muito mais difíceis se houvesse referências de app_b para app_a. Além disso, eu tinha um Meta.db_table definido manualmente para o meu modelo, o que poderia ter ajudado.
Notavelmente, você terá um histórico de migração alterado. Isso não importa, mesmo se você tiver um banco de dados com as migrações originais aplicadas. Se as migrações original e reescrita terminarem com o mesmo esquema de banco de dados, essa reescrita deverá estar OK.
fonte
Faça isso individualmente para cada modelo que precisa ser movido. Eu não sugeriria fazer o que a outra resposta diz mudando para números inteiros e retornando para chaves estrangeiras. Há uma chance de que novas chaves estrangeiras sejam diferentes e linhas possam ter IDs diferentes após as migrações e eu não queria correr nenhum risco de IDs incompatíveis ao voltar para chaves estrangeiras.
fonte