[PYTHON] Notes d'apprentissage pour la fonction migrations dans le framework Django (3)

Dans les deux notes précédentes, j'ai beaucoup appris sur la fonctionnalité de migration de django. Cette fois, apprenons à résoudre les problèmes de migrations utiles pour les opérations.

Environnement de développement
Mac OS:Sierra
python2.7.10
django1.11.2
mysql5.7.18

Ajouter un champ unique à l'état avec des données héritées

Description du problème: Les données ont été créées après que le site a été exploité pendant un certain temps. Ajoutez maintenant un nouveau champ unique, non nullable à votre modèle. Les données héritées (données existantes) doivent recevoir des valeurs par défaut pour que le modèle soit reflété dans la base de données. Cependant, attribuer la même valeur par défaut à plusieurs enregistrements de données enfreint la règle d'unicité et provoque une erreur. Si vous êtes dans un environnement de développement, vous pouvez supprimer toutes les bases de données et fichiers de migration et recréer la base de données, mais résolvons ce problème de manière plus gracieuse.

Problème de reproduction

Entrez dans le shell Django et créez les données de démonstration.

python manage.py shell
>>> from polls.models import Category, Article
>>> c = Category(name="game")
>>> c.save()
>>> a1 = Article(title="play Game1", text="Game1 is amazing!", category=c, image_url = "http://url1")
>>> a1.save()
>>> a2 = Article(title="play Game2", text="Game2 is good!", category=c, image_url = "http://url2")
>>> a2.save()
>>> a3 = Article(title="play Game3", text="Game3 is dump!", category=c, image_url = "http://url3")
>>> a3.save()

Vous pouvez voir les données créées par le client mysql (spécifiez d'abord la base de données du projet). Tout d'abord, les données de catégorie de blog:

mysql> select * from polls_category;
+----+------+
| id | name |
+----+------+
|  1 | game |
+----+------+
1 row in set (0.00 sec)

Ensuite, les données de phrase

mysql> select * from polls_article;
+----+------------+-------------------+-------------+-------------+
| id | title      | text              | category_id | image_url   |
+----+------------+-------------------+-------------+-------------+
|  1 | play Game1 | Game1 is amazing! |           1 | http://url1 |
|  2 | play Game2 | Game2 is good!    |           1 | http://url2 |
|  3 | play Game3 | Game3 is dump!    |           1 | http://url3 |
+----+------------+-------------------+-------------+-------------+
3 rows in set (0.00 sec)

Ajoutez maintenant un champ article_id au modèle Article pour identifier de manière unique l'article (contrairement à la clé primaire créée automatiquement). Ce champ répond aux exigences suivantes: --non-nullable (Null n'est pas autorisé car il s'agit d'un identifiant, une valeur par défaut est requise) --unique (le rend identifiable de manière unique) Si vous modifiez le fichier modèle en fonction de vos besoins:

models.py


class Article(models.Model):
    title = models.CharField(max_length=100)
    text = models.CharField(max_length=1000)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    image_url = models.URLField(max_length=200, default='toBeImplement')
    article_id = models.UUIDField(default=uuid.uuid4(), unique=True)

(Seule la classe d'article est affichée) Créez un fichier de migrations:

python manage.py makemigrations polls

Succès

Migrations for 'polls':
  polls/migrations/0003_article_article_id.py
    - Add field article_id to article

Essayer de refléter dans la base de données:

python manage.py migrate polls

J'obtiens une erreur (journal partiel)

...
django.db.utils.IntegrityError: (1062, "Duplicate entry '3059fd8259254f0693ce8d91cf1198cc' for key 'article_id'")

Pour clarifier l'erreur, jetez un œil au fichier de migrations 0003_article_article_id.py:

0003_article_article_id.py


# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-07-18 08:25
from __future__ import unicode_literals

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):

    dependencies = [
        ('polls', '0002_article_image_url'),
    ]

    operations = [
        migrations.AddField(
            model_name='article',
            name='article_id',
            field=models.UUIDField(default=uuid.UUID('3059fd82-5925-4f06-93ce-8d91cf1198cc'), unique=True),
        ),
    ]

La raison de l'erreur est que lorsque j'ai ajouté article_id à un enregistrement de données (article) existant, je lui ai donné la même valeur par défaut et violé l'exigence unique.

Comment réparer

Si vous disposez de données héritées, trois étapes sont nécessaires pour ajouter des champs uniques et non nullables. Reportez-vous au site officiel

Aperçu

Hypothèse: Ajout du champ correspondant au fichier modèle.

  1. Créez un fichier de migration qui correspond au changement de modèle et modifiez les attributs de champ de unique à nullable.
  2. Créez un fichier de migration vierge et attribuez une valeur par défaut aux données héritées.
  3. Créez un fichier de migration vierge et redéfinissez les attributs de champ sur uniques.

Détails

Étape 1: Comme mentionné ci-dessus, les champs ont déjà été ajoutés au modèle d'article et le fichier de migration 0003_article_article_id.py a également été créé. Par conséquent, modifiez le fichier de migrations 0003_article_article_id.py.

0003_article_article_id.py


# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-07-18 08:25
from __future__ import unicode_literals

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):

    dependencies = [
        ('polls', '0002_article_image_url'),
    ]

    operations = [
        migrations.AddField(
            model_name='article',
            name='article_id',
            field=models.UUIDField(default=uuid.UUID('3059fd82-5925-4f06-93ce-8d91cf1198cc'), null=True),
        ),
    ]

Autrement dit, remplacez "unique = True" dans la classe Migration-> operations-> migrations.AddField () par "null = True". Vous pouvez maintenant mettre les mêmes valeurs par défaut dans la nouvelle colonne des données héritées.

Étape 2: Créez un fichier de migration vierge.

python manage.py makemigrations polls --empty

Maintenant, mettez une valeur unique pour chaque enregistrement de données héritées dans une nouvelle colonne

0004_auto_20170718_0901.py


# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-07-18 09:01
from __future__ import unicode_literals

from django.db import migrations
import uuid


def gen_uuid(apps, schema_editor):
    Article = apps.get_model('polls', 'Article')
    for row in Article.objects.all():
        row.article_id = uuid.uuid4()
        row.save(update_fields=['article_id'])


class Migration(migrations.Migration):

    dependencies = [
        ('polls', '0003_article_article_id'),
    ]

    operations = [
        migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop)
    ]

Étape 3: Enfin, créez à nouveau un fichier de migration vide et redéfinissez les attributs de colonne sur unique. Vous pouvez maintenant limiter les colonnes de l'enregistrement de données nouvellement créé.

0005_auto_20170718_0901.py


# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-07-18 09:01
from __future__ import unicode_literals

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):

    dependencies = [
        ('polls', '0004_auto_20170718_0901'),
    ]

    operations = [
        migrations.AlterField(
            model_name='article',
            name='article_id',
            field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
    ]

Une fois le fichier de migrations prêt, reflétez les modifications apportées au modèle et aux données héritées.

(a_site) IT-02085-M:m_Migrate ruofan.ye$ python manage.py migrate polls
Operations to perform:
  Apply all migrations: polls
Running migrations:
  Applying polls.0003_article_article_id... OK
  Applying polls.0004_auto_20170718_0901... OK
  Applying polls.0005_auto_20170718_0901... OK

Succès, et si vous regardez la base de données

mysql> select * from polls_article;
+----+------------+-------------------+-------------+-------------+----------------------------------+
| id | title      | text              | category_id | image_url   | article_id                       |
+----+------------+-------------------+-------------+-------------+----------------------------------+
|  1 | play Game1 | Game1 is amazing! |           1 | http://url1 | 7347eab9e4df47eb989de30fc6baeec9 |
|  2 | play Game2 | Game2 is good!    |           1 | http://url2 | c4bac1cc1d3242aa934c9cb16f1cf5a9 |
|  3 | play Game3 | Game3 is dump!    |           1 | http://url3 | e6094b7b24fc46e7a59a7b90aaca298c |
+----+------------+-------------------+-------------+-------------+----------------------------------+
3 rows in set (0.00 sec)

Vous avez ajouté avec succès une nouvelle colonne à votre table et à vos données héritées. Créer de nouvelles données d'article:

>>> a4 = Article(title="play Game4", text="Game4 is new-feeling!", category=c, image_url = "http://url4")
>>> a4.save()

En regardant la base de données:

mysql> select * from polls_article;
+----+------------+-----------------------+-------------+-------------+----------------------------------+
| id | title      | text                  | category_id | image_url   | article_id                       |
+----+------------+-----------------------+-------------+-------------+----------------------------------+
|  1 | play Game1 | Game1 is amazing!     |           1 | http://url1 | 7347eab9e4df47eb989de30fc6baeec9 |
|  2 | play Game2 | Game2 is good!        |           1 | http://url2 | c4bac1cc1d3242aa934c9cb16f1cf5a9 |
|  3 | play Game3 | Game3 is dump!        |           1 | http://url3 | e6094b7b24fc46e7a59a7b90aaca298c |
|  4 | play Game4 | Game4 is new-feeling! |           1 | http://url4 | 0f1fcf070c544784b2a11ba2a4661cd7 |
+----+------------+-----------------------+-------------+-------------+----------------------------------+
4 rows in set (0.00 sec)

Le champ article_id a été implémenté comme prévu.

Recommended Posts

Notes d'apprentissage pour la fonction migrations dans le framework Django (2)
Notes d'apprentissage pour la fonction migrations dans le framework Django (3)
Notes d'apprentissage pour la fonction migrations dans le framework Django (1)
Notes diverses sur le framework Django REST
List, méthode pour les ressources imbriquées dans le framework Django REST
Résumé des points d'achoppement à Django pour la première fois
Notes de lancement pour les applications Django existantes
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 5 ~
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 4 ~
Instruction Loop For inversée dans un fichier HTML sur Django
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 1, 2, 3 ~
Changer la langue affichée dans Django 1.9
Historique d'apprentissage pour participer au développement d'applications d'équipe avec Python ~ Tutoriel Django 6 ~
Historique d'apprentissage pour participer au développement d'applications d'équipe en Python ~ Tutoriel Django 7 ~
Démarrez Django pour la première fois
Résumé des pages utiles pour étudier le framework d'apprentissage profond Chainer
Récupérer la chaîne de requête (chaîne de requête) avec Django
Obtenez l'adresse IP du client avec Django
Présentation du framework BOT Minette pour Python
Suppression logique dans Django, DRF (Django REST Framework)
Comprendre la commodité de Django Rest Framework
Django ~ Affichons-le sur le navigateur ~
Note de nfc.ContactlessFrontend () de nfcpy de python
Notes d'apprentissage depuis le début de Python 1
Remarques sur la création de fichiers statiques avec Django
Changer la liste dans l'instruction for
Paramètre d'attribut de même site du cookie dans Django
Obtenir les paramètres de requête pour les requêtes GET avec Django
MongoDB avec Python pour la première fois
Remarques sur l'utilisation de python (pydev) avec eclipse
Notes d'apprentissage depuis le début de Python 2
Essayez d'accéder à l'API Spotify dans Django.
[Renforcer l'apprentissage] Rechercher le meilleur itinéraire
Tout refaire pour l'écran de connexion Django
Mémo Django
J'ai essayé de prédire l'évolution de la quantité de neige pendant 2 ans par apprentissage automatique
Ingénierie des fonctionnalités pour l'apprentissage automatique à partir du 4e Google Colaboratory - Fonctionnalités interactives
Mémo Django
Comment installer le framework d'apprentissage en profondeur Tensorflow 1.0 dans l'environnement Windows Anaconda
La fonction _authenticate_with_backend était obsolète dans django auth.autenticate
CERTIFICATE_VERIFY_FAILED dans Python 3.6, le programme d'installation officiel de macOS
L'histoire selon laquelle le coût d'apprentissage de Python est faible
Conseils pour accéder à l'API ATND avec Python
Notes pour la mise en œuvre d'un co-filtrage simple en Python
Spécifiez l'URL de la vue dans le modèle Django
L'histoire de l'affichage des fichiers multimédias dans Django
Implémenter la fonctionnalité de connexion JWT dans le framework Django REST
[Implémentation pour l'apprentissage] Implémentation de l'échantillonnage stratifié en Python (1)
[Django] css dans le projet ne peut pas être lu
Faites une recherche ambiguë pour mysql dans Django
Notes de connaissances nécessaires pour comprendre le framework Python
Implémentation de la fonction d'authentification dans Django REST Framework à l'aide de djoser
[Pour les débutants] Introduction à la vectorisation dans l'apprentissage automatique
J'ai installé le framework Deep Learning Chainer
Reproduisons l'outil d'enseignement arithmétique "Jamaica" ❗️ vol.02 "Notes pour créer des fonctions en Python"
Reproduisons l'outil d'enseignement de l'arithmétique "Jamaica" ❗️ vol.01 "Notes pour l'affichage des images en Python"