Esses dias atrás recebemos a missão de atualizar o Django em um de nossos projetos para a sua versão mais nova e depois de muito esforço conseguimos. Deixo abaixo, anotado todos os passos que fizemos para conseguir tal resultado, para o caso de alguém também precisar, então vamos lá,
O South se foi:
A partir da versão 1.7 do Django, não precisamos mais usar o South, pois este foi “incorporado” dentro do Framework e a sintaxe das novas migrações são um pouco diferentes, por isso não são mais compatíveis com a sintaxe do South, sendo assim temos que gerar “essas novas migrações” e depois aplica-lás como “fakes” para só depois conseguirmos usar o novo módulo de migração.
Primeiro
- Tenha certeza que todas suas migrações antigas estejam aplicadas.
- IMPORTANTE: Se você usa controle de versão, faça uma nova branch git checkout -b update_django
e comece a trabalhar a partir deste ponto. Caso ocorra algum problema é só voltar para o branch anterior
- Crie um novo virtualenv, pois muitas apps e bibliotecas serão atualizadas e no caso de precisar voltar tudo, basta mudar para o env antigo.
- Atualize o Django.
1 |
$ pip install -U Django==1.10 |
- Vamos então fazer um backup das migrações antigas, para isso, cole o comando abaixo no seu terminal sem o $ inicial, :), substituindo o caminho abaixo pelo caminho do diretório que contém suas apps. Rode os comandos abaixo no seu shell.
1 |
$ for f in diretorio_seu_projeto/diretorio_apps/*/migrations; do echo "movendo $f para $f-south"; mv "$f" "$f-south"; done; |
- Depois do backup feito, criamos novamente as “pastas migrations vazias” dentro de cada app:
1 |
$ for f in diretorio_seu_projeto/diretorio_apps/*/; do echo "criando novo dir migrations em $f"; mkdir "$f/migrations"; done; |
- E criamos um arquivo __init__.py dentro de cada pasta migrations:
1 |
$ for f in diretorio_seu_projeto/diretorio_apps/*/migrations; do echo "criando novo __init__.py em $f"; touch "$f/__init__.py"; done; |
- Em settings.py retire a app ’south’ de INSTALLED_APPS
Fonte: https://docs.djangoproject.com/en/1.9/topics/migrations/#upgrading-from-south
- IMPORTANTE: Daqui para frente é bem provável que os dois comandos abaixo falharão devido ao fato de que ainda é preciso corrigir os pacotes, erros de importação, e muitos outros que irão surgir devido a atualização do Django. Portanto se preciso, corriga os erros e só depois rode os dois comandos abaixo para finalizar o upgrade das suas migrations
- Rode python manage.py makemigrations
. Django verá as pastas migration vazias e criará novas initial migrations no novo formato.
- Rode python manage.py migrate --fake-initial
. E pronto, seu projeto está apto a criar novas migrações.
Segundo
Aqui está listado as apps que precisei atualizar, para funcionar com Django 1.10
1 2 3 4 5 6 7 8 9 10 |
$ pip install -U django_extensions # o problema descrito neste link vai acontecer se você não atualizar django_extensions -> https://github.com/django-extensions/django-extensions/issues/776 $ pip install -U sorl-thumbnail $ pip install -U django-haystack $ pip install -U django-debug-toolbar $ pip uninstall django-ckeditor-updated $ pip install django-ckeditor $ pip install -U django-import-export $ pip install -U djangorestframework $ pip install -U django-filter $ pip install -U django-suit |
Abaixo alguns erros que aconteceram comigo :(, mas felizmente deu tudo certo no final
Primeiro erro:
Retirar tudo de __init__.py e coloque dentro de um arquivo com o nome de apps.py, pois a partir das novas versões, caso você tente carregar alguma model dentro de __init__.py esta não estará disponível, por isso devemos carregá-la no ready de uma classe que estende AppConfig. O arquivo apps.py deve ficar dentro da app em questão. No exemplo abaixo import integracoes.signals_connectors que estava dentro de __init__.py deve ficar como no exemplo abaixo:
1 2 3 4 5 6 |
from __future__ import unicode_literals from django.apps import AppConfig class ErpConfig(AppConfig): def ready(self): import integracoes.signals_connectors |
Segundo erro:
Erro: django.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the ‘fields’ attribute or the ‘exclude’ attribute is prohibited; form CampanhaConfiguracoesGeralAdminForm needs updating.
Em todos os ModelForm coloque o campo fields caso este não exista:
1 2 3 4 |
class MeuForm(forms.ModelForm): class Meta: .... fields = "__all__" # coloque o campos especificos ou __all__ para incluir tudo |
Fonte: https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#selecting-the-fields-to-use
Terceiro erro:
Remover todos os imports from django.conf.urls import patterns
Erro: TypeError: view must be a callable or a list/tuple in the case of include().
1 2 3 4 5 6 7 8 |
from app.views import MinhaView class MyModelAdmin(admin.ModelAdmin): def get_urls(self): urls = super(MyModelAdmin, self).get_urls() # my_urls = [ url(r'^my_view/$', 'app.views.MinhaView'), ] # deixe de fazer assim my_urls = [ url(r'^my_view/$', MinhaView.as_view()), ] # e faça assim agora, importanto a classe antes return my_urls + urls |
Ou em urls.py:
1 2 3 |
from app.views import MinhaView # my_urls = [ url(r'^$', 'app.views.MinhaView', name='home'), ] # deixe de fazer assim my_urls = [ url(r'^$', MyView.as_view(), name='home'), ] # e faça assim agora, importanto a View no início |
Quarto erro:
Em seu settings, mude a configuração do seus templates para:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ # insert your TEMPLATE_DIRS here ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this # list if you haven't customized them: 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', ], }, }, ] |
Fonte: https://docs.djangoproject.com/en/1.10/ref/templates/upgrading/
Quinto erro:
Erro: <class ‘minha_app.admin.MeuAdmin’>: (admin.E111) The value of ‘list_display_links[0]‘ refers to ‘None’, which is not defined in ‘list_display’.
Se dentro de alguma classe admin.ModelAdmin estiver a propriedade list_display_links setada desta forma:
1 2 3 4 |
class MeuAdmin(admin.ModelAdmin): .... # list_display_links = (None, ) # comente esta linha. list_display_links = None # e sete para None, sem a Tupla. |
Fonte: http://stackoverflow.com/questions/35184004/how-to-avoid-admin-e111-error-in-django-1-9-2
Sexto erro:
Erro: (urls.E004) Your URL pattern (‘^’, (<module ‘meu_path.urls’ from ‘/home/meu_user/projetos/meu_path/urls.pyc’>, None, None)) is invalid. Ensure that urlpatterns is a list of url() instances. HINT: Try using url() instead of a tuple.
Mude qualquer tupla dentro de urlpatterns
1 |
urlpatterns = [ (r'^$', home, name='home'), # tuple ] |
para url() instâncias:
1 |
urlpatterns = [ url(r'^$', home, name='home'), # url instance ] |
Sétimo erro:
Erro: meu.path.suamodel: (fields.E160) The options auto_now, auto_now_add, and default are mutually exclusive. Only one of these options may be present.
mude:
1 |
data = models.DateTimeField(u'Cadastro', auto_now_add=True, null=True, default=None) |
para:
1 |
cadastro = models.DateTimeField(u'Cadastro', auto_now_add=True, null=True) |
Fonte: https://code.djangoproject.com/ticket/24678
Oitavo erro:
Erro: AttributeError: ‘Options’ object has no attribute ‘module_name’
Mude:
1 |
Model._meta.module_name |
Para:
1 |
Model._meta.model_name |
Fonte: https://docs.djangoproject.com/en/1.8/internals/deprecation/
Nono erro:
Erro: … object has no attribute ‘get_query_set’ class ConfigManager(models.Manager):
mude:
1 |
self.get_query_set().first() |
para:
1 |
self.get_queryset().first() |
Fonte: https://docs.djangoproject.com/en/1.8/releases/1.8/#features-removed-in-1-8
Décimo erro:
Erro: set_autocommit got an unexpected keyword argument ‘force_begin_transaction_with_broken_autocommit’
Deixe de usar django-transaction-hooks e use o listener transaction.on_commit() nativo do novo Django.
Fonte 1: https://stackoverflow.com/questions/34114408/set-autocommit-got-an-unexpected-keyword-argument-force-begin-transaction-with/34114416#34114416
Fonte 2: https://docs.djangoproject.com/en/1.9/topics/db/transactions/#django.db.transaction.on_commit
Décimo primeiro erro:
Erro: ‘future’ is not a registered tag library. Must be one of:
Remova todos os imports {% load url from future %} de seus templates pois foi depreciado.
Décimo segundo erro:
Erro: Django error: render_to_response() got an unexpected keyword argument ‘context_instance’
1 2 3 4 5 6 |
from django.shortcuts import render def my_view(request): context = {'foo': 'bar'} # return render_to_response(request, 'my_template.html', context, context_instance=RequestContext(request)) # mude esta linha return render(request, 'my_template.html', context) # e faça desta forma agora |
Décimo Terceiro erro:
Erro: ‘The number of GET/POST parameters exceeded ‘ TooManyFieldsSent: The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.
Em seu settings.py coloque a propriedade acima para None, assim DATA_UPLOAD_MAX_NUMBER_FIELDS = None
Fonte: https://docs.djangoproject.com/en/1.10/ref/settings/#data-upload-max-number-fields
Se você usa DRF (Django Rest Framework), com certeza você obterá erros abaixo:
Erro: ‘GetProdutoDetailView’ should either include a queryset
attribute, or override the get_queryset()
method.
1 2 3 4 5 6 |
class MyView(...): # troque: # model = MyModel # por: queryset = MyModel.objects.all() |
Erro: request.DATA
has been deprecated in favor of request.data
since version 3.0, and has been fully removed as of version 3.2.
Basta substituir request.DATA por request.data em suas API Views.
Fonte: http://www.django-rest-framework.org/topics/3.2-announcement/#deprecations