Débutants en Python accro à Django

introduction

~~ Vous avez dit "Django a beaucoup d'experts dans l'entreprise" !!!! ~~

Future Advent Calendar 2019 Ceci est l'article du 10ème jour. Le calendrier de l'Avent de cette année fait peur car il y a trop de choses ...

Cette fois, j'ai écrit du golang et des rails, Ce sera la première fois que je construirai un système avec Python ~~ rushing ~~, et je publierai ce à quoi j'étais accro.

En une ligne

Le modèle de Django ne reflète pas les paramètres tels que ceux par défaut dans la base de données!

Environnement prérequis

L'environnement est basé sur l'hypothèse que Introduction à Python Django (1) est terminé dans l'ordre jusqu'à l'article (6). Ce n'est pas une référence officielle, mais c'était un bon article. Je suis heureux que le produit fini se trouve sur https://github.com/kakky/mybook20. (La vérification suivante est effectuée avec ce commit. Https://github.com/kakky/mybook20/commit/82e741652bfd7f82f5c0bc601e04b7585632d266)

Si vous avez cloné ce qui précède, procédez comme suit comme préparation préliminaire.

$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver

Si vous trébuchez ici, veuillez vous référer à l'article supposé. La commande ci-dessus est écrite dans cet article. Introduction à Python Django (3)

Sujet principal

Le modèle Django ne reflète pas les paramètres tels que la valeur par défaut dans DB

Préparation

Modifions le class Book de cms / models.py comme suit.

cms/models.py


class Book(models.Model):
    """Livres"""
    name = models.CharField('Titre de livre', max_length=255, unique=True)
    publisher = models.CharField('l'éditeur', max_length=255)
    page = models.IntegerField('nombre de pages', blank=True, default=0)
    on_sale = models.BooleanField('Vente', default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

Ajoutez created_at et ʻupdated_at, remplissez l'heure actuelle lors de l'insertion et de la mise à jour respectivement, ajoutez ʻon_sale et laissez la valeur par défaut définie sur True.

De plus, ʻunique = True est ajouté à nom`. Par défaut, toutes les colonnes ont «null = False», qui est le comportement de la contrainte non nulle.

Depuis que nous avons modifié le modèle, créez un fichier de migration comme suit. Je suis en colère de remplir la valeur initiale, alors définissez-la correctement comme timezone.now.

$ python manage.py makemigrations
You are trying to add the field 'created_at' with 'auto_now_add=True' to book without a default; the database needs something to populate existing rows.

 1) Provide a one-off default now (will be set on all existing rows)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
You can accept the default 'timezone.now' by pressing 'Enter' or you can provide another value.
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
[default: timezone.now] >>> timezone.now

Appliquer la migration.

python manage.py migrate

Contrôle de fonctionnement

Accédez à http://127.0.0.1:8000/admin/ et ajoutez un livre. (Détails omis)

success.png

Jetons un coup d'œil au contenu de la base de données.

$ sqlite> select * from cms_book;
id|name|page|created_at|on_sale|updated_at|publisher
1|La chérie du diable|100|2019-12-09 13:03:29.102973|1|2019-12-09 13:03:29.132151|Kakugawa Bunko

Il contient created_at updated_at et a l'air bien. * ʻOn_sale` vaut 1 et représente True. Maintenant, insérons-le directement dans la base de données.

sqlite> insert into cms_book(name, publisher, page) values('Chanson de la main du diable', 'Shueisha', 200);
Error: NOT NULL constraint failed: cms_book.created_at

Pour une raison quelconque, je me fâche que created_at, qui devrait entrer automatiquement l'heure actuelle, viole la contrainte non nulle. Donc, remplissez created_at, ʻupdated_at` avec CURRENT_TIMESTAMP.

sqlite> insert into cms_book(
    name,
    publisher,
    page,
    created_at,
    updated_at
)
values(
    'Chanson de la main du diable',
    'Shueisha',
    200,
    CURRENT_TIMESTAMP,
    CURRENT_TIMESTAMP
)
;
Error: NOT NULL constraint failed: cms_book.on_sale

Cette fois, je me fâche que on_sale, qui aurait dû spécifier la valeur par défaut, soit null. Comme cela ne peut pas être aidé, ʻon_sale` spécifie également explicitement la valeur.

sqlite> insert into cms_book(
    name,
    publisher,
    page,
    on_sale,
    created_at,
    updated_at
)
values(
    'Chanson de la main du diable',
    'Shueisha',
    200,
    0,
    CURRENT_TIMESTAMP,
    CURRENT_TIMESTAMP
)
;
sqlite> select * from cms_book;
id|name|page|created_at|on_sale|updated_at|publisher
1|La chérie du diable|100|2019-12-09 13:03:29.102973|1|2019-12-09 13:03:29.132151|Kakugawa Bunko
2|Chanson de la main du diable|200|2019-12-09 13:12:17|0|2019-12-09 13:12:17|Shueisha

Cette fois, j'ai pu insérer avec succès. __ Pourquoi la contrainte «NOT NULL» est-elle apparue dans une colonne avec «default», «auto_now_add », «auto_now»? __ Je vais les examiner dans l'ordre.

sqlite> .schema cms_book
CREATE TABLE IF NOT EXISTS "cms_book"(
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(255) NOT NULL UNIQUE,
    "page" integer NOT NULL,
    "created_at" datetime NOT NULL,
    "on_sale" bool NOT NULL,
    "updated_at" datetime NOT NULL,
    "publisher" varchar(255) NOT NULL
)
;

Au moment de la définition de la table, les systèmes default et ʻauto_now` ont déjà été abandonnés. Vérifiez le fichier de migration et le sql généré.

cms/migrations/0002_auto_20191209_2202.py


    operations = [
        migrations.AddField(
            model_name='book',
            name='created_at',
            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='book',
            name='on_sale',
            field=models.BooleanField(default=True, verbose_name='Vente'),
        ),
        migrations.AddField(
            model_name='book',
            name='updated_at',
            field=models.DateTimeField(auto_now=True),
        ),
        migrations.AlterField(
            model_name='book',
            name='name',
            field=models.CharField(max_length=255, unique=True, verbose_name='Titre de livre'),
        ),
        migrations.AlterField(
            model_name='book',
            name='publisher',
            field=models.CharField(max_length=255, verbose_name='l'éditeur'),
        ),
    ]
$ python manage.py showmigrations
admin
 [X] 0001_initial
(réduction)
cms
 [X] 0001_initial
 [X] 0002_auto_20191209_2202
(réduction)
$ python manage.py sqlmigrate cms 0002_auto_20191209_2202
BEGIN;
(réduction)
--
-- Alter field publisher on book
--
CREATE TABLE "new__cms_book"(
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(255) NOT NULL UNIQUE,
    "page" integer NOT NULL,
    "created_at" datetime NOT NULL,
    "on_sale" bool NOT NULL,
    "updated_at" datetime NOT NULL,
    "publisher" varchar(255) NOT NULL
)
;
INSERT INTO "new__cms_book"(
    "id",
    "name",
    "page",
    "created_at",
    "on_sale",
    "updated_at",
    "publisher"
)
SELECT
    "id",
    "name",
    "page",
    "created_at",
    "on_sale",
    "updated_at",
    "publisher"
FROM
    "cms_book"
;
DROP TABLE "cms_book"
;
ALTER TABLE "new__cms_book" RENAME TO "cms_book"
;
COMMIT
;

Dans le fichier de migration, vous pouvez voir que la notation reste comme default = True. J'ai omis le sql généré car il est plus long (je ne sais pas pourquoi vous vous embêtez à créer, supprimer, modifier) Veuillez prêter attention aux pièces suivantes.

CREATE TABLE "new__cms_book"(
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(255) NOT NULL UNIQUE,
    "page" integer NOT NULL,
    "created_at" datetime NOT NULL,
    "on_sale" bool NOT NULL,
    "updated_at" datetime NOT NULL,
    "publisher" varchar(255) NOT NULL
)

La valeur default et ʻauto_now` ont chuté avec brio. À propos, ce comportement était le même non seulement dans sqlite mais aussi dans postgresql etc.

Solution

Au début, je ne pouvais pas le trouver facilement même si je cherchais sur Google, mais ce phénomène a été mentionné dans ce débordement de pile. https://stackoverflow.com/questions/53706125/django-default-at-database

En d'autres termes, "le modèle et le schéma de base de données sont des mons différents. Les valeurs non nulles et uniques seront appliquées, mais la valeur par défaut est impossible!" ...

Je pense qu'il existe de nombreux cas où une base de données est référencée / mise à jour à partir de plusieurs applications ou corrigée manuellement, mais je me demande si les utilisateurs de Django auront des problèmes ... Pour contourner le problème, il semble préférable de générer régulièrement du DDL (SQL brut) à partir d'ERD au lieu du DDL généré à partir du modèle.

finalement

J'ai été surpris de voir à quel point FW permet un tel comportement avec des problèmes de praticité. J'ai réaffirmé qu'il est plus important de supprimer les parties de basse couche telles que la lecture de SQL brut pour résoudre les problèmes.

Recommended Posts

Débutants en Python accro à Django
Django 1.11 a démarré avec Python3.6
Faites Django avec CodeStar (Python3.8, Django2.1.15)
Python3 + Django ~ Mac ~ avec Apache
Premiers pas avec Python Django (1)
Premiers pas avec Python Django (4)
Premiers pas avec Python Django (3)
Introduction à Python Django (6)
Hello World (débutant) avec Django
Premiers pas avec Python Django (5)
[Python] Qu'est-ce qu'une instruction with?
python + django + scikit-learn + mecab (1) avec heroku
Que faire avec la sortie de PYTHON?
Exécutez python3 Django1.9 avec mod_wsgi (déployer)
Notes sur l'utilisation de rstrip avec python.
Premiers pas avec Python 3.8 sous Windows
Python | Ce que vous pouvez faire avec Python
[Memo] Tweet sur Twitter avec Python
Notez ce que vous avez fait pour utiliser Flycheck avec Python
Premiers pas avec le framework Python Django sur Mac OS X
[Python] Rétrospective de ce que j'ai enseigné aux débutants en programmation à partir des fonctions
INSÉRER dans MySQL avec Python [Pour les débutants]
Exécutez le servo avec Python sur ESP32 (Windows)
[Épisode 2] Les débutants ont essayé Numeron AI avec python
[Épisode 3] Les débutants ont essayé Numeron AI avec python
Django + Apache avec mod_wsgi sur Windows Server 2016
Un mémo contenant Python2.7 et Python3 dans CentOS
Carte des informations de location sur une carte avec python
Chase des applications actives sur Mac avec Python
Remarques sur la réalisation de l'OCR japonais avec Python
Si scipy est python 2.7.8, l'installation de pip trébuche
CentOS 6.4, Python 2.7.3, Apache, mod_wsgi, Django
Télécharger des fichiers sur le Web avec Python
Ce que j'ai obtenu de Python Boot Camp
Créer un environnement Python avec Anaconda sur Mac
[Épisode 0] Un débutant a essayé Numeron AI avec python
[Épisode 1] Un débutant a essayé Numeron AI avec python
Ce que j'ai fait avec les tableaux Python
[Python] Lire des images avec OpenCV (pour les débutants)
Un rappel de ce que je suis resté coincé lors du démarrage d'Atcoder avec python
Création WebApi avec Python (création CRUD) Pour les débutants
Téléchargez ce que vous avez dans la demande vers S3 avec AWS Lambda Python
Comment les débutants en Python commencent avec Progete
Janken Poi avec Python Exécutons sur un serveur local Windows pour les débutants
Installation de PIL avec Python 3.x sur macOS
[Pour les débutants] Essayez le web scraping avec Python
Travailler avec le GPS en Python pour Raspberry Pi 3
Ce que j'ai fait quand je suis resté coincé dans le délai avec lambda python
Démarrer avec Python avec 100 coups sur le traitement du langage
Jusqu'à ce que vous définissiez ce que vous avez fait avec Django avec Jenkins
Je ne peux pas créer de projet avec Python3.5 (Windows) + django1.7.1.
Créer un environnement python avec pyenv sur EC2 (ubuntu)
Créez un environnement python avec ansible sur centos6
Gagnez l'application Web Python + Flask avec Jenkins
[Dernière histoire] Un débutant a essayé Numeron AI avec python
Installez OpenCV 4.0 et Python 3.7 sur Windows 10 avec Anaconda
[Python] Créer un environnement de développement Django avec Docker
~ Conseils pour les débutants de Python donnés avec amour par Pythonista ① ~
Créer un environnement de Nginx + uWSGI + Python (Django) avec docker
Folium: Visualisez les données sur une carte avec Python
Essayez de travailler avec Mongo en Python sur Mac