[PYTHON] Mettre à jour les applications exécutées sur Django 1.7 vers Django 1.8

Qu'est-ce que c'est?

Calendrier de l'Avent du groupe mixi C'est le deuxième jour.

"Ticket Camp" exploité par Mixy Group est développé avec Python et Django côté serveur.

J'utilise actuellement Django version 1.7.11, qui est une note de ce que j'ai fait lors de la mise à jour vers Django 1.8.

Je suis sûr que certaines parties sont uniques aux camps de billets, mais j'espère que cela aidera ceux qui envisagent de mettre à jour Django.

Politique de mise à niveau de la version Ticket Camp

En premier lieu, la politique de mise à niveau de la bibliothèque de framework de ticket camp dépend quelque peu de mon intuition, et elle est difficile à clarifier, mais généralement les trois règles suivantes sont utilisées. Je peux expliquer.

Le troisième "à moins qu'il n'y ait une raison spécifique" est ambiguë, et la raison est claire dans des cas tels que "Je veux utiliser la fonction X, mais dans ce cas, je dois mettre à jour la bibliothèque Y". Cependant, il y a des cas où il n'y a pas de raison plus profonde que "j'ai essayé de le soulever en passant" comme "Il y avait un cas pour modifier l'API de téléchargement d'image, donc je mettrai à jour Pillow aussi".

Quoi qu'il en soit, je tiens à souligner que nous ne développons pas avec une politique telle que "interdire la mise à niveau de version".

Le deuxième "suivre les mises à jour majeures autant que possible" signifie qu'en tant que personne qui a utilisé Django avant la version 1.0, il est préférable de toujours suivre la dernière version pour maintenir la santé de l'application, et lors de la mise à jour Il est basé sur la règle empirique selon laquelle c'est facile après tout quand cela devient.

De ce point de vue, je voulais mettre à jour depuis Django 1.8 Beta Stage, mais avec quelques échecs de mise à jour Il y avait un point que je n'avais pas touché depuis longtemps. En attendant, Django 1.9 RC est sorti, donc c'est mauvais. J'ai finalement commencé à travailler sérieusement sur la mise à jour de la version.

Puis, lorsque j'ai vérifié le site Django alors que j'allais appuyer sur le bouton de publication de cet article, [Django 1.9 était déjà publié](https://www.djangoproject.com/weblog/2015/dec/01/] django-19-publié /) ・ ・ ・

Correctifs nécessaires pour mettre à jour vers Django 1.8

Mettez à jour Celery vers la dernière version

Au ticket camp, la file d'attente de travaux utilise le standard Celery en Python et le traitement qui nécessite des E / S réseau externes telles que la notification push via AWS SNS , Le traitement de la liste des tickets / de la correspondance des demandes, etc., qui prend un certain temps, sont traités par le processus de travail de Celery.

Lorsque je suis passé à Django 1.8, une erreur s'est produite dans le fichier céleri == 3.1.7 que j'utilisais jusqu'à présent, j'ai donc mis à jour vers la dernière version de céleri == 3.1.9.

pip install --upgrade celery==3.1.9

Celery est très stable et a beaucoup d'utilisateurs, donc je ne ressens pas beaucoup de risques en ce qui concerne les mises à jour mineures.

Mise à jour de django_nose vers la dernière version

Le lanceur de tests unitaires utilise le lanceur de tests django_nose au lieu de celui standard de Django.

Comme cela a également été corrigé sur une version légèrement plus ancienne, une erreur s'est produite, j'ai donc mis à jour vers la dernière version django_nose == 1.4.2.

pip install --upgrade django_nose==1.4.2

Remplacez «TEST_NAME» par «TEST»

À partir de Django 1.7, la méthode de configuration de la base de données de test est passée du préfixe TEST_ à l'utilisation d'un dict indépendant appelé TEST](https://docs.djangoproject.com/en/1.8/ref/settings) / # test), donc corrigez-le à ce moment.

Avant correction.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'root',
        'NAME': 'ticketcamp_dev',
        'TEST_NAME': 'test_ticketcamp_dev',
    },
}

Modifié.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'root',
        'NAME': 'ticketcamp_dev',
        'TEST': {
            'NAME': 'test_ticketcamp_dev',
        },
    },
}

loader.get_template_from_string a disparu

Il n'y avait qu'un seul endroit dans le camp de tickets qui utilisait la méthode django.template.loader.get_template_from_string.

Au minimum, le code ressemble à ceci:

from django.template import loader, Context

tmpl = loader.get_template_from_string(content)
output = tmpl.render(Context(ctxt))

Code comme ça

AttributeError: 'module' object has no attribute 'get_template_from_string'

J'ai commencé à avoir l'erreur.

Apparemment, django.template.loader.get_template_from_string a été supprimé dans Django 1.8.

Ce type de suppression soudaine de méthode est rare dans Django et ne se produit jamais dans les API publiques. Normalement, un DeprecatedWarning est affiché et sera aboli dans Django 1.x, vous le remarquerez donc pendant le développement normal.

Cela signifie que cette méthode a été traitée comme une méthode privée et non comme une méthode publique.

Heureusement, dans ce cas, remplacer loader.get_template_from_string par Template était une solution simple.

from django.template import Template, Context

tmpl = Template(content)
output = tmpl.render(Context(ctxt))

L'avertissement de ForeignKey (unique = True) est sorti.

Ticket camp utilise django.contrib.auth pour l'enregistrement et l'authentification des utilisateurs, mais c'est un vestige du développement depuis l'époque de Django 1.4, Pattern utilisant l'objet Profile pour donner des informations supplémentaires aux utilisateurs reste toujours.

Le code ressemble à ceci:

# -*- coding: utf-8 -*-
#Ce code a un piège alors ne le copiez pas!
from django.conf import settings
from django.db import models

class Profile(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, unique=True)
    #réduction

Après la mise à jour vers Django 1.8 et l'exécution de nœuds de calcul Celery, j'ai reçu l'avertissement suivant:

myapp.Profile.user: (fields.W342) Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.
    HINT: ForeignKey(unique=True) is usually better served by a OneToOneField.

Je le savais pour la première fois à ce stade, mais le code ci-dessus est en premier lieu

# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import models

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    #réduction

Il semble que j'aurais dû utiliser ʻOneToOneField () au lieu de ForeignKey (unique = True) `comme dans. De plus, parce que j'ai fait une erreur dans la première étape, il y avait des endroits où des avertissements similaires ont été émis.

Il me semblait que je devrais le changer en ʻOneToOneField () `comme dans l'indication dans l'avertissement, donc j'ai pu tout réparer à cette occasion.

default, ʻauto_now, ʻauto_now_add of DateTimeField est maintenant exclusif

De là, c'est la production de mises à jour difficiles.

La définition du modèle de ticket camp a le modèle suivant, et la date et l'heure de création du modèle ainsi que la date et l'heure de mise à jour sont automatiquement mises à jour en tant que created_at, ʻupdated_at`.

from django.db import models

class Ticket(models.Model):
    price = models.IntegerField()
    count = models.IntegerField()
    created_at = models.DateTimeField(default=timezone.now, auto_now_add=True)
    updated_at = models.DateTimeField(default=timezone.now, auto_now=True)

Lorsque j'ai essayé d'exécuter du code contenant une telle définition de modèle sur Django 1.8, le travailleur Celery a arrêté de démarrer en raison de l'erreur suivante:

myapp.Ticket.created_at: (fields.E160) The options auto_now, auto_now_add, and default are mutually exclusive. Only one of these options may be present.
myapp.Ticket.updated_at: (fields.E160) The options auto_now, auto_now_add, and default are mutually exclusive. Only one of these options may be present.

La cause est que, comme indiqué dans le message d'erreur, ʻauto_now, ʻauto_now_add et default sont des options mutuellement exclusives et ne peuvent pas être spécifiées en même temps.

Pour résoudre ce problème, j'ai pensé aux solutions suivantes.

La première option est que je ne veux pas écrire le code moi-même autant que possible, et que Django a fait une telle modification est une bonne correspondance pour ʻauto_now, ʻauto_now_add, default en même temps. J'ai abandonné parce que j'avais deviné qu'il serait difficile de faire une telle implémentation.

La deuxième option devait avoir moins de changements de code et moins d'impact sur l'application, mais je l'ai poussée jusqu'à "ne pas définir de schémas qui ne peuvent pas être représentés par le modèle Django". Je l'ai mis en attente du principe de conception.

Nous testons actuellement la mise à jour avec un troisième correctif.

Tout d'abord, dans l'exemple du modèle ci-dessus, created_at peut supprimer en toute sécurité l'option ʻauto_now_add = True`.

Lorsque vous créez une instance du modèle, l'option default option timezone.now est évaluée et la valeur de created_at devient l'heure actuelle, et cette valeur est enregistrée dans la base de données lorsque vous appelezsave (). Parce que ça le sera. (En y réfléchissant, je pense que ʻauto_now_add = True` était inutile en premier lieu.)

Le problème est ʻupdated_at`

updated_at = models.DateTimeField(default=timezone.now)

Ou

updated_at = models.DateTimeField(auto_now=True)

Tu dois choisir.

Dans le premier cas, ʻupdated_at est nonNone lorsque l'instance est créée, mais save () ne met pas à jour la colonne ʻupdated_at de la base de données.

$ python manage.py shell
>>> from myapp.models import Ticket
>>> ticket = Ticket(price=1000, count=1)
>>> ticket.updated_at is not None
True
>>> ticket.save()
>>> ticket.price = 1500
>>> ticket.save()  #Je l'ai changé, mais la base de données a été mise à jour_à n'est pas mis à jour

J'ai envisagé des moyens de mettre à jour ʻupdated_aten utilisant [les signauxpost_save`](https://docs.djangoproject.com/en/1.8/ref/signals/#post-save) , Je pensais que cela poserait un problème car il était difficile de trouver la cause, alors j'ai décidé d'y renoncer.

Ce dernier, l'inconvénient de sélectionner DateTimeField (auto_now = True), est que ʻupdated_at est Nonelorsque l'instance est créée, et l'heure actuelle est le seul moment oùsave () est appelé. Cela signifie que le comportement sera reflété dans la propriété updated_at.

$ python manage.py shell
>>> from myapp.models import Ticket
>>> ticket = Ticket(price=1000, count=1)
>>> ticket.updated_at is not None
False  # updated_à est toujours Aucun
>>> ticket.save()
>>> ticket.updated_at is not None
True   # save()Devenez non-None pour la première fois après avoir appelé

En d'autres termes, il y a des cas où cela ne fonctionnera pas s'il y a du code qui fait référence à l'instance ʻupdated_at avant save () `.

Cependant, chaque méthode a des avantages et des inconvénients, et au final, la dernière méthode semblait suivre l'usage attendu par le créateur du framework, j'ai donc adopté la dernière méthode et adopté la méthode suivante. J'ai décidé de le changer avec un sentiment.

from django.db import models

class Ticket(models.Model):
    price = models.IntegerField()
    count = models.IntegerField()
    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(auto_now=True)

Puisque l'erreur évidente a disparu, lorsque j'ai commencé à vérifier l'opération, il semble qu'elle fonctionne normalement. Après cela, j'ai pensé que si je faisais référence à ʻupdated_at avant save () ʻet déplaçais la partie où l'erreur s'est produite, je pensais que la mise à jour était terminée, mais il y avait encore des pièges ...

Les questions non résolues

En fait, je voulais atteindre le point où la mise à jour était terminée au moment de la sortie de ce calendrier de l'Avent, mais il y a encore des problèmes non résolus et inexpliqués.

Ajouter des migrations

Lorsque vous exécutez le test unitaire,

$ python manage.py test myapp
#Abréviation
django.db.utils.IntegrityError: (1215, 'Cannot add foreign key constraint')

J'ai eu l'erreur.

Il a fallu beaucoup de temps pour rechercher la cause, mais il semble que la cause soit que le schéma ne soit pas reflété correctement lors de la création de la base de données de test unitaire car les migrations n'existent pas sous chaque répertoire de l'application Django.

Ticket camp a été développé avant l'introduction de Migrations dans Django 1.7, et même lors de la mise à jour vers Django 1.7, "Migrations" Je n'ai pas fait manage.py make migrations parce que je n'avais pas prévu de l'utiliser, mais il semble que j'ai finalement besoin d'utiliser Migrations.

Correction de YAML pour les luminaires

Revenez au programme d'exécution de test standard de Django, créez manuellement une base de données de test, puis

$ python manage.py test myapp --keepdb

Lorsque j'ai exécuté le test avec l'option --keepdb, j'ai pu exécuter le test.

Cependant, comme je m'y attendais à l'avance, la définition du modèle de ʻupdated_at a changé, donc j'essayais d'INSÉRER NULL dans ʻupdated_at et je n'ai pas pu charger les appareils.

Concernant ce problème

--Fix YAML pour tous les appareils.

Nous envisageons une méthode de correction telle que.

Avertissement pour les fonctions qui seront obsolètes dans Django 1.9

Avec Django 1.8, il y a beaucoup d'avertissements pour les fonctions qui seront supprimées dans la 1.9, donc je les écrase un par un.

Avertissement pour django.core.cache.get_cache.

RemovedInDjango19Warning: 'get_cache' is deprecated in favor of 'caches'

Avant correction.

from django.core.cache improt get_cache

get_cache('default').get('key')

Modifié. Cela a été remplacé mécaniquement.

from django.core.cache improt caches

caches['default'].get('key')

Avertissement pour django.utils.functional.memoize.

RemovedInDjango19Warning: memoize wrapper is deprecated and will be removed in Django 1.9. Use django.utils.lru_cache instead.

Avertissement pour django.utils.datastructures.SortedDict. Ce n'est pas parce que le code que j'ai écrit moi-même, mais parce que la version de django-redis que j'utilise est assez ancienne, alors je l'ai mise à jour. Cela semble nécessaire.

RemovedInDjango19Warning: SortedDict is deprecated and will be $
emoved in Django 1.9.

Un avertissement qui se produit lors du pickleing d'un modèle Django.

RuntimeWarning: Pickled model instance's Django version is not specified.

finalement

Il semble que nous soyons encore au milieu de la mise à jour, mais selon la politique de publication de Django, la 1.7 actuellement utilisée mettra fin à la maintenance lorsque la 2.0 sortira, il est donc absolument nécessaire de passer à la prochaine version 1.8 de LTS. Il y a.

Nous étudions également s'il est possible de migrer de Python 2.7 vers Python 3.5, et je ressens le besoin de suivre la dernière version de Django pour la migration vers Python 3.5.

J'espère régler la question d'ici la fin de l'année et accueillir confortablement la nouvelle année.

Ensuite, akkuma écrira sur le mode Action pour Android.

Recommended Posts

Mettre à jour les applications exécutées sur Django 1.7 vers Django 1.8
Migrer les applications Django exécutées sur Python 2.7 vers Python 3.5
mettre à jour django version 1.11.1 vers 2.2
Mettez à jour le python que vous aviez sur votre Mac à 3.7-> 3.8
mise à jour de Django
Mettre à jour Mac Python de 2 à 3
L'histoire de l'échec de la mise à jour de "calendar.day_abbr" sur l'écran d'administration de django
Comment mettre à jour PHP sur Amazon Linux 2
Comment mettre à jour la sécurité sur CentOS Linux 8
Comment mettre à jour la sécurité sur Ubuntu 19.10 Eoan Ermine
Comment mettre à jour easy_install
Notes de céleri sur Django
Exécutez Django sur PythonAnywhere
Comment mettre à jour Spyder
Comment créer un environnement Django (python) sur Docker
Mémo de déploiement de Django × Postgresql sur Docker vers Heroku
impossible d'importer django
Exécution de MINST dans TensorFlow 2.0 et visualisation dans TensorBoard (version 2019)
Hello World avec Django
Comment utiliser Django avec Google App Engine / Python
Mettre à jour vscode sur Linux
Transition vers l'écran de mise à jour avec le Django a tag
Comment exécuter Django sur IIS sur un serveur Windows
Ne perdez pas contre Ruby! Comment exécuter Python (Django) sur Heroku
Introduction à Python Django (2) Win
[Django] Remarques sur l'utilisation de django-debug-toolbar
Shell pour créer un projet django
Comment s'inscrire auprès de pypi
Comment mettre à jour avec SQLAlchemy?
Mettre à jour python-social-auth de 0.1.x à 0.2.x
Développement de l'environnement Django sur Windows 10
Installez Django sur Mac
Passer du texte à Django genericview
Déployer le projet django sur heroku
Hello World (débutant) avec Django
Comment créer des couches AWS Lambda lors de l'exécution de sélénium × chrome sur AWS Lambda
Précautions lors de l'exécution de Python sur EC2 à partir d'AWS Lambda (Exécuter la commande)
Comment déployer une application Django sur heroku en seulement 5 minutes
Comment mettre à jour la version Python de Cloud Shell dans GCP
Tout, de la création d'un environnement Python à son exécution sous Windows