Python-Anfänger süchtig nach Django

Einführung

~~ Du hast gesagt "Django hat viele Experten in der Firma" !!!! ~~

Future Advent Calendar 2019 Dies ist der Artikel am 10. Tag. Der diesjährige Adventskalender ist beängstigend, weil es zu viele Sachen gibt ...

Dieses Mal schrieb ich Golang und Rails, Es wird das erste Mal sein, dass ein System mit Python ~~ Rushing ~~ erstellt wird, und ich werde veröffentlichen, wovon ich süchtig war.

In einer Zeile

Das Modell von Django spiegelt keine Einstellungen wie die Standardeinstellungen in der Datenbank wider!

Voraussetzung Umgebung

Die Umgebung basiert auf der Annahme, dass Einführung in Python Django (1) in der Reihenfolge bis zum Artikel (6) abgeschlossen ist. Es ist keine offizielle Referenz, aber es war ein guter Artikel. Ich bin froh, dass das fertige Produkt unter https://github.com/kakky/mybook20 verfügbar ist. (Die folgende Überprüfung wird mit diesem Commit durchgeführt. Https://github.com/kakky/mybook20/commit/82e741652bfd7f82f5c0bc601e04b7585632d266)

Wenn Sie das oben Gesagte geklont haben, gehen Sie als vorläufige Vorbereitung wie folgt vor.

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

Wenn Sie hier stolpern, lesen Sie bitte den angenommenen Artikel. Der obige Befehl ist in diesem Artikel geschrieben. Einführung in Python Django (3)

Hauptthema

Das Django-Modell spiegelt keine Einstellungen wie die Standardeinstellungen in der Datenbank wider

Vorbereitung

Lassen Sie uns das "Klassenbuch" von "cms / models.py" wie folgt ändern.

cms/models.py


class Book(models.Model):
    """Bücher"""
    name = models.CharField('Buchtitel', max_length=255, unique=True)
    publisher = models.CharField('der Herausgeber', max_length=255)
    page = models.IntegerField('Seitenzahl', blank=True, default=0)
    on_sale = models.BooleanField('Verkauf', default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

Fügen Sie created_at und updated_at hinzu, geben Sie die aktuelle Zeit beim Einfügen bzw. Aktualisieren ein, fügen Sie on_sale hinzu und belassen Sie den Standardwert auf True.

Zusätzlich wird name mit unique = True hinzugefügt. Standardmäßig haben alle Spalten "null = False", was das Verhalten der Nicht-Null-Einschränkung ist.

Da wir das Modell geändert haben, erstellen Sie eine Migrationsdatei wie folgt. Ich bin wütend, den Anfangswert einzugeben, also setze ihn auf "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

Migration anwenden.

python manage.py migrate

Funktionsprüfung

Gehen Sie zu http://127.0.0.1:8000/admin/ und fügen Sie ein Buch hinzu. (Details weggelassen)

success.png

Werfen wir einen Blick auf den Inhalt der Datenbank.

$ sqlite> select * from cms_book;
id|name|page|created_at|on_sale|updated_at|publisher
1|Der Liebling des Teufels|100|2019-12-09 13:03:29.102973|1|2019-12-09 13:03:29.132151|Kakugawa Bunko

Es enthält created_at updated_at und sieht gut aus. * On_sale ist 1 und steht für True. Fügen wir es nun direkt in die DB ein.

sqlite> insert into cms_book(name, publisher, page) values('Teufelshandlied', 'Shueisha', 200);
Error: NOT NULL constraint failed: cms_book.created_at

Aus irgendeinem Grund ärgere ich mich darüber, dass created_at, das automatisch die aktuelle Zeit eingeben sollte, die Nicht-Null-Einschränkung verletzt. Versuchen Sie also, created_at und updated_at mit CURRENT_TIMESTAMP zu füllen.

sqlite> insert into cms_book(
    name,
    publisher,
    page,
    created_at,
    updated_at
)
values(
    'Teufelshandlied',
    'Shueisha',
    200,
    CURRENT_TIMESTAMP,
    CURRENT_TIMESTAMP
)
;
Error: NOT NULL constraint failed: cms_book.on_sale

Dieses Mal ärgere ich mich, dass on_sale, das den Standardwert hätte angeben sollen, null ist. Da es keine Hilfe dafür gibt, gibt on_sale auch den Wert explizit an.

sqlite> insert into cms_book(
    name,
    publisher,
    page,
    on_sale,
    created_at,
    updated_at
)
values(
    'Teufelshandlied',
    'Shueisha',
    200,
    0,
    CURRENT_TIMESTAMP,
    CURRENT_TIMESTAMP
)
;
sqlite> select * from cms_book;
id|name|page|created_at|on_sale|updated_at|publisher
1|Der Liebling des Teufels|100|2019-12-09 13:03:29.102973|1|2019-12-09 13:03:29.132151|Kakugawa Bunko
2|Teufelshandlied|200|2019-12-09 13:12:17|0|2019-12-09 13:12:17|Shueisha

Diesmal konnte ich erfolgreich einfügen. __ Warum ist in einer Spalte mit "default", "auto_now_add", "auto_now" "NOT NULL Constraint" aufgetreten? __ __ Ich werde sie der Reihe nach anschauen.

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
)
;

Zum Zeitpunkt der Tabellendefinition sind die Systeme default und auto_now bereits gelöscht. Überprüfen Sie die Migrationsdatei und die generierte SQL.

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='Verkauf'),
        ),
        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='Buchtitel'),
        ),
        migrations.AlterField(
            model_name='book',
            name='publisher',
            field=models.CharField(max_length=255, verbose_name='der Herausgeber'),
        ),
    ]
$ python manage.py showmigrations
admin
 [X] 0001_initial
(Kürzung)
cms
 [X] 0001_initial
 [X] 0002_auto_20191209_2202
(Kürzung)
$ python manage.py sqlmigrate cms 0002_auto_20191209_2202
BEGIN;
(Kürzung)
--
-- 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
;

In der Migrationsdatei können Sie sehen, dass die Notation wie folgt lautet: "default = True". Ich habe das generierte SQL weggelassen, weil es länger ist (ich weiß nicht, warum Sie sich die Mühe machen, es zu erstellen, zu löschen, zu ändern). Bitte beachten Sie die folgenden Teile.

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
)

Der Standardwert und auto_now sind brillant gesunken. Übrigens war dieses Verhalten nicht nur in SQLite, sondern auch in Postgresql usw. dasselbe.

Lösung

Anfangs konnte ich es nicht leicht finden, selbst wenn ich gegoogelt habe, aber dieses Phänomen wurde in diesem Stapelüberlauf erwähnt. https://stackoverflow.com/questions/53706125/django-default-at-database

Mit anderen Worten, "Modell und DB-Schema sind unterschiedlich mon. Nicht null und eindeutig werden angewendet, aber Standard ist unmöglich!" ...

Ich denke, dass es viele Fälle gibt, in denen eine Datenbank aus mehreren Anwendungen referenziert / aktualisiert oder manuell gepatcht wird, aber Django-Benutzer werden keine Probleme haben ... Um dieses Problem zu umgehen, scheint es besser zu sein, DDL (Raw SQL) kontinuierlich aus ERD zu generieren, als DDL, das aus einem Modell generiert wurde.

Schließlich

Ich war überrascht, wie beliebt FW ein solches Verhalten mit praktischen Problemen zulässt. Ich habe erneut bekräftigt, dass es wichtiger ist, Low-Layer-Teile wie das Lesen von Raw-SQL zu unterdrücken, um Probleme zu lösen.

Recommended Posts

Python-Anfänger süchtig nach Django
Django 1.11 wurde mit Python3.6 gestartet
Mach Django mit CodeStar (Python3.8, Django2.1.15)
Python3 + Django ~ Mac ~ mit Apache
Erste Schritte mit Python Django (1)
Erste Schritte mit Python Django (4)
Erste Schritte mit Python Django (3)
Einführung in Python Django (6)
Hallo Welt (Anfänger) mit Django
Erste Schritte mit Python Django (5)
[Python] Was ist eine with-Anweisung?
Python + Django + Scikit-Learn + Mecab (1) mit Heroku
Was tun mit PYTHON Release?
Führen Sie python3 Django1.9 mit mod_wsgi aus (deploy)
Hinweise zur Verwendung von rstrip mit Python.
Erste Schritte mit Python 3.8 unter Windows
Python | Was Sie mit Python machen können
[Memo] Tweet auf Twitter mit Python
Beachten Sie, was Sie getan haben, um Flycheck mit Python zu verwenden
Erste Schritte mit dem Python-Framework Django unter Mac OS X.
[Python] Rückblickend auf das, was ich Programmieranfängern aus Funktionen beigebracht habe
INSERT in MySQL mit Python [Für Anfänger]
Führen Sie das Servo mit Python unter ESP32 (Windows) aus.
[Episode 2] Anfänger haben Numeron AI mit Python ausprobiert
[Episode 3] Anfänger haben Numeron AI mit Python ausprobiert
Django + Apache mit mod_wsgi unter Windows Server 2016
Ein Memo mit Python2.7 und Python3 in CentOS
Kartenmietinformationen auf einer Karte mit Python
Verfolgen Sie aktive Anwendungen auf einem Mac mit Python
Hinweise zur japanischen OCR mit Python
CentOS 6.4, Python 2.7.3, Apache, mod_wsgi, Django
Laden Sie mit Python Dateien im Web herunter
Was ich von Python Boot Camp bekommen habe
Erstellen Sie eine Python-Umgebung mit Anaconda auf einem Mac
[Episode 0] Anfänger haben Numeron AI mit Python ausprobiert
[Episode 1] Anfänger haben Numeron AI mit Python ausprobiert
Was ich mit Python-Arrays gemacht habe
[Python] Bilder mit OpenCV lesen (für Anfänger)
Eine Erinnerung an das, was ich beim Starten von Atcoder mit Python feststeckte
WebApi-Erstellung mit Python (CRUD-Erstellung) Für Anfänger
Laden Sie das, was Sie angefordert haben, mit AWS Lambda Python in S3 hoch
Wie Python-Anfänger mit Progete beginnen
Janken Poi mit Python Lassen Sie uns für Anfänger auf einem lokalen Windows-Server laufen
PIL mit Python 3.x unter macOS installieren
[Für Anfänger] Versuchen Sie Web Scraping mit Python
Arbeiten mit GPS in Python für Raspberry Pi 3
Was ich getan habe, als ich mit Lambda Python im Zeitlimit steckte
Erste Schritte mit Python mit 100 Klopfen bei der Sprachverarbeitung
Bis du weißt, was du mit Django mit Jenkins gemacht hast
Ich kann mit Python3.5 (Windows) + django1.7.1 kein Projekt erstellen.
Erstellen Sie eine Python-Umgebung mit pyenv auf EC2 (Ubuntu)
Erstellen Sie eine Python-Umgebung mit ansible auf centos6
Gewinnen Sie die Python + Flask-Web-App mit Jenkins
[Letzte Geschichte] Anfänger haben Numeron AI mit Python ausprobiert
Installieren Sie OpenCV 4.0 und Python 3.7 unter Windows 10 mit Anaconda
[Python] Erstellen Sie mit Docker eine Django-Entwicklungsumgebung
~ Tipps für Python-Anfänger mit Liebe von Pythonista ① ~
Erstellen Sie mit Docker eine Umgebung aus Nginx + uWSGI + Python (Django)
Folium: Visualisieren Sie Daten auf einer Karte mit Python
Versuchen Sie, mit Mongo in Python auf dem Mac zu arbeiten