~~ 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.
Le modèle de Django ne reflète pas les paramètres tels que ceux par défaut dans la base de données!
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)
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
Accédez à http://127.0.0.1:8000/admin/ et ajoutez un livre. (Détails omis)
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.
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.
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.