Recentemente, mudei do Django 1.6 para o 1.7 e comecei a usar migrações (nunca usei o South).
Antes de 1.7, eu costumava carregar os dados iniciais com um fixture/initial_data.json
arquivo, que era carregado com o python manage.py syncdb
comando (ao criar o banco de dados).
Agora, comecei a usar migrações e este comportamento está obsoleto:
Se um aplicativo usa migrações, não há carregamento automático de acessórios. Como as migrações serão necessárias para aplicativos no Django 2.0, este comportamento é considerado obsoleto. Se você deseja carregar os dados iniciais de um aplicativo, considere fazer isso em uma migração de dados. ( https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )
A documentação oficial não tem um exemplo claro de como fazer isso, então minha pergunta é:
Qual é a melhor maneira de importar esses dados iniciais usando migrações de dados:
- Escreva o código Python com várias chamadas para
mymodel.create(...)
, - Use ou escreva uma função Django ( como chamar
loaddata
) para carregar dados de um arquivo de fixação JSON.
Prefiro a segunda opção.
Eu não quero usar o South, já que Django parece ser capaz de fazer isso nativamente agora.
Respostas:
Atualização : Veja o comentário de @GwynBleidD abaixo para os problemas que esta solução pode causar, e veja a resposta de @ Rockallite abaixo para uma abordagem que é mais durável para futuras mudanças de modelo.
Supondo que você tenha um arquivo de fixação em
<yourapp>/fixtures/initial_data.json
Crie sua migração vazia:
No Django 1.7:
No Django 1.8+, você pode fornecer um nome:
Edite seu arquivo de migração
<yourapp>/migrations/0002_auto_xxx.py
2.1. Implementação personalizada, inspirada em Django '
loaddata
(resposta inicial):2.2. Uma solução mais simples para
load_fixture
(por sugestão de @juliocesar):Útil se você deseja usar um diretório personalizado.
2.3. Mais simples: chamar
loaddata
comapp_label
luminárias de carga vontade do<yourapp>
'sfixtures
dir automaticamente:Se você não especificar
app_label
, loaddata tentará carregar ofixture
nome do arquivo de todos os diretórios de fixtures do aplicativo (o que você provavelmente não deseja).Executá-lo
fonte
loaddata('loaddata', fixture_filename, app_label='<yourapp>')
também irá diretamente para o diretório do fixture do aplicativo (portanto, não há necessidade de construir o caminho completo do fixture)models.py
arquivos atuais , que podem ter alguns campos extras ou algumas outras alterações. Se algumas alterações foram feitas após a criação da migração, ela falhará (portanto, não podemos nem mesmo criar migrações de esquema após essa migração). Para consertar isso, podemos alterar teporalmente o registro de aplicativos em que o serializador está trabalhando para o registro fornecido para a função de migração no primeiro parâmetro. O registro para o caminho está localizado emdjango.core.serializers.python.apps
.app registry
, sem alterar uma variável global (o que poderia causar problemas em um futuro hipotético com migrações de banco de dados paralelas).Versão curta
Você NÃO deve usar o
loaddata
comando de gerenciamento diretamente em uma migração de dados.Versão longa
loaddata
utiliza odjango.core.serializers.python.Deserializer
que usa os modelos mais atualizados para desserializar dados históricos em uma migração. Esse é um comportamento incorreto.Por exemplo, suponha que haja uma migração de dados que utiliza o
loaddata
comando de gerenciamento para carregar dados de um aparelho, e já está aplicado em seu ambiente de desenvolvimento.Posteriormente, você decide adicionar um novo campo obrigatório ao modelo correspondente, então você o faz e faz uma nova migração em relação ao seu modelo atualizado (e possivelmente fornece um valor único para o novo campo quando
./manage.py makemigrations
solicitado).Você executa a próxima migração e está tudo bem.
Finalmente, você concluiu o desenvolvimento de seu aplicativo Django e o implementou no servidor de produção. Agora é a hora de você executar todas as migrações do zero no ambiente de produção.
No entanto, a migração de dados falha . Isso ocorre porque o modelo desserializado do
loaddata
comando, que representa o código atual, não pode ser salvo com dados vazios para o novo campo obrigatório adicionado. O acessório original não possui os dados necessários para isso!Mas mesmo se você atualizar o aparelho com os dados necessários para o novo campo, a migração de dados ainda falhará . Quando a migração de dados está em execução, a próxima migração que adiciona a coluna correspondente ao banco de dados, ainda não é aplicada. Você não pode salvar dados em uma coluna que não existe!
Conclusão: em uma migração de dados, o
loaddata
comando introduz uma possível inconsistência entre o modelo e o banco de dados. Definitivamente, você NÃO deveusá-lo diretamente em uma migração de dados.A solução
loaddata
comando depende dadjango.core.serializers.python._get_model
função para obter o modelo correspondente de um acessório, que retornará a versão mais atualizada de um modelo. Precisamos fazer um patch de macacos para obter o modelo histórico.(O código a seguir funciona para Django 1.8.x)
fonte
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
o mesmo problema queloaddata
? Ouignorenonexistent=True
cobre todos os problemas possíveis?ignorenonexistent=True
argumento tem dois efeitos: 1) ignora os modelos de um acessório que não estão nas definições de modelo mais atuais, 2) ignora os campos de um modelo de um acessório que não estão na definição de modelo correspondente mais atual. Nenhum deles lida com a situação do novo campo obrigatório no modelo . Então, sim, acho que sofre do mesmo problema que o normalloaddata
.natural_key()
, que esse método não parece suportar - eu apenas substituí o valor natural_key pelo id real do modelo referenciado.Inspirado por alguns dos comentários (nomeadamente n__o's) e pelo fato de ter muitos
initial_data.*
arquivos espalhados por vários aplicativos, decidi criar um aplicativo Django que facilitaria a criação dessas migrações de dados.Usando Django-migration-dispositivo elétrico você pode simplesmente executar o seguinte comando de gerenciamento e irá procurar através de todo o seu
INSTALLED_APPS
parainitial_data.*
arquivos e transformá-los em migrações de dados.Veja django-migration-fixture para instruções de instalação / uso.
fonte
Para dar ao seu banco de dados alguns dados iniciais, escreva uma migração de dados. Na migração de dados, use a função RunPython para carregar seus dados.
Não escreva nenhum comando loaddata, pois essa forma está obsoleta.
Suas migrações de dados serão executadas apenas uma vez. As migrações são uma sequência ordenada de migrações. Quando as migrações 003_xxxx.py são executadas, django migrações grava no banco de dados que este aplicativo é migrado até este (003), e executará apenas as seguintes migrações.
fonte
myModel.create(...)
(ou usando um loop) na função RunPython?As soluções apresentadas acima não funcionaram para mim, infelizmente. Descobri que toda vez que mudo meus modelos, tenho que atualizar meus acessórios. Idealmente, em vez disso, eu escreveria migrações de dados para modificar os dados criados e os dados carregados pelo aparelho de maneira semelhante.
Para facilitar isso , escrevi uma função rápida que irá procurar no
fixtures
diretório do aplicativo atual e carregar um fixture. Coloque esta função em uma migração no ponto do histórico do modelo que corresponde aos campos na migração.fonte
RunPython(load_fixture('badger', 'stoat'))
. gist.github.com/danni/1b2a0078e998ac080111Na minha opinião, os jogos estão um pouco ruins. Se o seu banco de dados muda com frequência, mantê-los atualizados logo será um pesadelo. Na verdade, não é só minha opinião, no livro "Two Scoops of Django" é explicado muito melhor.
Em vez disso, escreverei um arquivo Python para fornecer a configuração inicial. Se você precisar de algo mais, sugiro que dê uma olhada no Factory boy .
Se você precisa migrar alguns dados, você deve usar migrações de dados .
Também há "Queime seus acessórios , use fábricas de modelos" sobre o uso de acessórios.
fonte
No Django 2.1, eu queria carregar alguns modelos (como nomes de países, por exemplo) com dados iniciais.
Mas eu queria que isso acontecesse automaticamente logo após a execução das migrações iniciais.
Portanto, pensei que seria ótimo ter uma
sql/
pasta dentro de cada aplicativo que exigisse o carregamento dos dados iniciais.Então, dentro dessa
sql/
pasta, eu teria.sql
arquivos com os DMLs necessários para carregar os dados iniciais nos modelos correspondentes, por exemplo:Para ser mais descritivo, esta é a aparência de um aplicativo contendo uma
sql/
pasta:Também encontrei alguns casos em que precisava que os
sql
scripts fossem executados em uma ordem específica. Portanto, decidi prefixar os nomes dos arquivos com um número consecutivo, como pode ser visto na imagem acima.Então eu precisava de uma maneira de carregar qualquer
SQLs
disponível dentro de qualquer pasta de aplicativo automaticamente fazendopython manage.py migrate
.Portanto, criei outro aplicativo chamado
initial_data_migrations
e o adicionei à lista deINSTALLED_APPS
nosettings.py
arquivo. Em seguida, criei umamigrations
pasta dentro e adicionei um arquivo chamadorun_sql_scripts.py
( que na verdade é uma migração personalizada ). Como pode ser visto na imagem abaixo:Criei
run_sql_scripts.py
para que ele cuide da execução de todos ossql
scripts disponíveis dentro de cada aplicativo. Este é então disparado quando alguém correpython manage.py migrate
. Este costumemigration
também adiciona os aplicativos envolvidos como dependências, dessa forma, ele tenta executar assql
instruções somente depois que os aplicativos necessários executaram suas0001_initial.py
migrações (não queremos tentar executar uma instrução SQL em uma tabela inexistente).Aqui está a fonte desse script:
Espero que alguém ache isso útil, funcionou muito bem para mim !. Se você tiver alguma dúvida, por favor me avise.
NOTA: Esta pode não ser a melhor solução, uma vez que estou apenas começando com o django, no entanto, ainda quero compartilhar este "Como fazer" com todos vocês, já que não encontrei muitas informações enquanto pesquisava sobre isso no Google.
fonte