Γράφοντας το πρώτο σας Django app, μέρος 2

Ο οδηγός αυτός ξεκινά εκεί που τελειώνει ο οδηγός 1. Εδώ, θα ασχοληθούμε με τις ρυθμίσεις της βάσης δεδομένων, θα δημιουργήσουμε το πρώτο μας μοντέλο και θα κάνουμε μια μικρή εισαγωγή στο διαχειριστικό site (admin site) του Django, το οποίο δημιουργείται αυτόματα.

Ρύθμιση της Βάσης Δεδομένων

Στον προηγούμενο οδηγό δεν ασχοληθήκαμε καθόλου με το αρχείο mysite/settings.py παρόλο που είπαμε ότι αποτελεί την καρδιά ενός project. Το αρχείο αυτό αποτελεί ένα κοινό Python module στου οποίου οι μεταβλητές (variables) αναπαριστούν τις ρυθμίσεις του Django. Ανοίξτε το αρχείο αυτό.

Προεπιλεγμένα, οι ρυθμίσεις αφορούν την βάση δεδομένων SQLite. Αν είστε νέος στις βάσεις δεδομένων ή απλά ενδιαφέρεστε στο να δοκιμάσετε το Django, τότε αυτή είναι η πιο εύκολη επιλογή. Η SQLite περιλαμβάνεται στην Python, με αποτέλεσμα να μην χρειάζεται να εγκαταστήσετε τίποτε άλλο για να υποστηρίξετε την βάση δεδομένων σας. Όταν ξεκινήσετε το πρώτο σας πραγματικό project, ωστόσο, ίσως χρειαστεί να χρησιμοποιήσετε από την αρχή μια πιο εύρωστη (robust) βάση δεδομένων όπως η PostgreSQL, για να αποφύγετε τυχόν πονοκεφάλους που θα προκύψουν κατά την αλλαγή βάσεων δεδομένων (από SQLite στην PostgreSQL) στο μέλλον.

Αν επιθυμείτε να χρησιμοποιήσετε διαφορετική βάση δεδομένων από την SQLite, εγκαταστήστε πρώτα τις κατάλληλες βιβλιοθήκες βάσης δεδομένων και μετά αλλάξτε τα ακόλουθα keys στη ρύθμιση DATABASES 'default' ούτως ώστε να ταιριάζουν με τη δική σας σύνδεση στη βάση δεδομένων:

  • ENGINE – Είτε 'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', ή 'django.db.backends.oracle'. Άλλα backends είναι επίσης διαθέσιμα.
  • NAME – Το όνομα της βάσης δεδομένων σας. Αν χρησιμοποιείτε την SQLite, το όνομα της βάσης θα είναι το ίδιο με το όνομα του αρχείου στον υπολογιστή σας. Σε αυτή την περίπτωση, το NAME θα είναι το πλήρες μονοπάτι (path) συμπεριλαμβανομένου και του ονόματος+κατάληξη του αρχείου. Η προεπιλεγμένη τιμή, os.path.join(BASE_DIR, 'db.sqlite3'), θα αποθηκεύσει το αρχείο στο root φάκελο του project σας.

Αν δεν χρησιμοποιείτε την SQLite, τότε θα πρέπει να δηλώσετε επιπρόσθετα στοιχεία όπως USER, PASSWORD και HOST. Για περισσότερες πληροφορίες, δείτε στην αντίστοιχη ενότητα του εγχειριδίου (documentation) σχετικά με DATABASES.

Για βάσεις δεδομένων πλην της SQLite

Αν χρησιμοποιείτε βάση δεδομένων διαφορετική από την SQLite, σιγουρευτείτε ότι έχετε δημιουργήσει κάποια βάση πριν προχωρήσετε. Κάντε το με την εντολή «CREATE DATABASE database_name;» μέσα από την interactive κονσόλα της βάσης δεδομένων σας.

Επίσης σιγουρευτείτε ότι ο χρήστης (user) που δηλώσατε στην ρύθμιση USER στο αρχείο mysite/settings.py έχει δικαιώματα «create database». Αυτό θα μας επιτρέψει την αυτόματη δημιουργία μιας test database η οποία θα μας χρειαστεί αργότερα στον οδηγό.

Αν χρησιμοποιείτε την SQLite, δεν χρειάζεται να δημιουργήσετε τίποτα εκ των προτέρων. Το αρχείο της βάσης δεδομένων θα δημιουργηθεί αυτόματα όταν χρειαστεί.

Όσο επεξεργάζεστε το αρχείο mysite/settings.py, θέστε τη ρύθμιση TIME_ZONE στη δική σας ζώνη ώρας (π.χ για την Ελλάδα αυτό θα γίνει 'Europe/Athens').

Επίσης, σημειώστε τη ρύθμιση INSTALLED_APPS στην αρχή του αρχείου. Αυτή η ρύθμιση κρατάει τα ονόματα όλων των Django applications που θα ενεργοποιηθούν κατά τη λειτουργία του Django (με άλλα λόγια αυτά που θα είναι διαθέσιμα). Τα apps μπορουν να χρησιμοποιηθούν σε πολλαπλά projects και μπορείτε επίσης να τα πακετάρετε και να τα διανέμετε για χρήση από άλλους χρήστες στα δικά τους projects.

Από προεπιλογή, η ρύθμιση INSTALLED_APPS περιέχει τα ακόλουθα apps, όπου όλα έρχονται προεγκατεστημένα με το Django:

Αυτά τα applications συμπεριλαμβάνονται στο Django ως διευκόλυνση καθώς χρησιμοποιούνται όλα συνήθως.

Επιπροσθέτως, μερικά από αυτά τα applications χρειάζονται τουλάχιστον έναν πίνακα στη βάση δεδομένων (database table). Για το λόγο αυτό, θα πρέπει να δημιουργήσουμε αυτούς του πίνακες στη βάση μας, πριν ξεκινήσουμε να τα χρησιμοποιούμε. Για να το κάνετε αυτό, τρέξτε την ακόλουθη εντολή (από κονσόλα και ευρισκόμενοι στο root φάκελο του project σας):

$ python manage.py migrate
...\> py manage.py migrate

Η εντολή migrate κοιτάζει στη ρύθμιση INSTALLED_APPS και δημιουργεί τυχόν απαραίτητους πίνακες, βάση της ρύθμισης DATABASES του αρχείου mysite/settings.py και βάση των database migrations τα οποία υπάρχουν σε κάθε app (θα το δούμε αυτό σε λίγο). Θα δείτε ένα μήνυμα για κάθε migration που ολοκληρώνεται. Αν ενδιαφέρεστε, τρέξτε την εντολή (μέσα από την interactive κονσόλα της βάσης δεδομένων σας) \dt (για PostgreSQL), SHOW TABLES; (για MySQL), .schema (για SQLite) ή SELECT TABLE_NAME FROM USER_TABLES; (για Oracle) για να σας εμφανιστούν οι πίνακες που δημιούργησε το Django για σας.

Για τους μινιμαλιστές

Όπως είπαμε παραπάνω, οι προεπιλεγμένες εφαρμογές περιλαμβάνονται επειδή χρησιμοποιούνται συχνά, αλλά δεν τις χρειάζονται όλοι. Αν δεν χρειάζεστε κάποια ή καμία από αυτές, μπορείτε ελεύθερα να τις ορίσετε ως σχόλιο ή να διαγράψετε την αντίστοιχη γραμμή (γραμμές) από τη ρύθμιση INSTALLED_APPS πριν τρέξετε την εντολή migrate. Η djadmin:migrate θα τρέξει τα migrations μόνο για τις εφαρμογές που βρίσκονται στη ρύθμιση INSTALLED_APPS (και φυσικά δεν είναι σχόλια).

Δημιουργώντας τα μοντέλα

Σε αυτό το στάδιο θα ορίσουμε τα μοντέλα – κατ” ουσίαν, το layout της βάσης δεδομένων, με μερικά πρόσθετα metadata.

Φιλοσοφία

Ένα μοντέλο είναι η μοναδική και απόλυτη πηγή των πραγματικών δεδομένων σας. Περιέχει τα ουσιώδη χαρακτηριστικά (πεδία) και συμπεριφορές (μέθοδοι) των δεδομένων που αποθηκεύετε. Το Django ακολουθεί την αρχή του DRY. Ο στόχος είναι να ορίσετε (δηλώσετε) τα μοντέλα σας σε ένα μέρος και αυτόματα να αντλείτε πράγματα από εκεί.

Τα migrations (που είδαμε παραπάνω) περιλαμβάνονται (ως έννοια) στα μοντέλα - για παράδειγμα, εν αντίθεση με την γλώσσα προγραμματισμού Ruby On Rails, τα migrations προκύπτουν αποκλειστικά από τα μοντέλα σας και ουσιαστικά αποτελούν ένα είδος ιστορικού το οποίο το Django μπορεί να ανατρέξει και να αλλάξει το schema της βάσης δεδομένων σας που να ταιριάζει στα τρέχοντα μοντέλα σας.

Στην απλή εφαρμογή ψηφοφορίας μας, θα δημιουργήσουμε δύο μοντέλα: το Question και το Choice. Το Question μοντέλο θα έχει (ως χαρακτηριστικά-πεδία) μια ερώτηση και μια ημερομηνία κυκλοφορίας (έκδοσης) αυτής. Το Choice μοντέλο θα έχει δύο χαρακτηριστικά-πεδία: το κείμενο της απάντησης-επιλογής και ένα μετρητή για να κρατάει τον αριθμό που αυτή η απάντηση επιλέχθηκε. Κάθε Choice θα συνδέεται με το μοντέλο Question.

Αυτές οι έννοιες αναπαρίστανται στο Django με απλές Python classes. Επεξεργαστείτε το αρχείο polls/models.py ούτως ώστε να δείχνει κάπως έτσι:

polls/models.py
from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Ο παραπάνω κώδικας φαίνεται ευθύς, καθαρός και κατανοητός. Κάθε μοντέλο αντιπροσωπεύεται από μία class η οποία κάνει subclass την django.db.models.Model class. Κάθε μοντέλο έχει έναν αριθμό από class variables (χαρακτηριστικά), όπου καθένα από αυτά αναπαριστά ένα πεδίο στη βάση δεδομένων. Με άλλα λόγια κάθε class variable αντιστοιχεί σε μια στήλη στη βάση δεδομένων (database column).

Κάθε πεδίο αναπαρίσταται από ένα instance της Field class – π.χ, η κλάση CharField δηλώνει πεδίο χαρακτήρων ενώ η κλάση DateTimeField δηλώνει πεδίο για ημερομηνίες. Αυτό λέει στο Django τον τύπο των δεδομένων που θα διατηρεί κάθε πεδίο. Με αυτό τον τρόπο, δηλαδή, χτίζουμε το schema της database μας. Τα δύο αυτά μοντέλα είναι στην ουσία δύο πίνακες στην βάση δεδομένων, τα attributes τους είναι οι στήλες στον εκάστοτε πίνακα και ο τύπος των δεδομένων που θα κρατάει κάθε στήλη δηλώνεται με τα instances της κλάσης Field. Βεβαίως κάθε instance της κλάσης Field δέχεται κάποια ορίσματα (όπως, πχ, το μέγιστο αριθμό χαρακτήρων που θα μπορεί να κρατάει κάθε ερώτηση κλπ) τα οποία θέτουν κάποια επιπλέον χαρακτηριστικά (άλλα είναι απαραίτητα, άλλα προαιρετικά).

Το όνομα κάθε Field instance (π.χ``question_text`` ή pub_date) αποτελεί και το όνομα του field , σε μια φιλική προς τη μηχανή μορφή. Θα τη χρησιμοποιήσετε αυτή την τιμή όποτε αναφέρεστε σε αυτή τη στήλη στον Python κώδικα σας ενώ το ίδιο όνομα θα χρησιμοποιηθεί ως όνομα στήλης στην βάση δεδομένων.

Μπορείτε να χρησιμοποιήσετε ένα προαιρετικό πρώτο positional argument στη κλάση Field για να καθορίσετε το όνομα του για τον χρήστη (όχι για τη μηχανή, όπως πριν). Αυτό χρησιμοποιείται σε μερικά εσωτερικά μέρη του Django και βοηθάει στο να αναγνωρίζει κανείς το πεδίο αυτό. Αν αυτό το πεδίο δεν οριστεί τότε το Django θα χρησιμοποιήσει τη μορφή για τη μηχανή. Σε αυτό το παράδειγμα, έχουμε ορίσει το φιλικό-προς-τον-χρήστη όνομα μόνο για το πεδίο Question.pub_date. Για όλα τα υπόλοιπα πεδία αυτού του μοντέλου (Question.question_text) θα χρησιμοποιηθεί το φιλικό-προς-τη-μηχανή όνομα.

Όπως είπαμε παραπάνω, μερικά Field classes απαιτούν να έχουν κάποια arguments συμπληρωμένα. Για παράδειγμα το CharField, απαιτεί να το δώσετε ένα max_length. Αυτό δεν χρησιμοποιείται μόνο για το schema της βάσης δεδομένων αλλά και για το validation, όπως θα δούμε σύντομα.

Ένα Field μπορεί να έχει διάφορα προαιρετικά arguments. Σε αυτό το παράδειγμα έχουμε θέσει στο votes πεδίο την τιμή 0 (μηδέν) μέσω του argument default.

Τέλος, σημειώστε ότι η συσχέτιση (μεταξύ των δύο μοντέλων) επιτυγχάνεται χρησιμοποιώντας την κλάση ForeignKey. Αυτό λέει στο Django ότι κάθε Choice σχετίζεται με μια Question ενώ αντίστροφα κάθε Question μπορεί να συσχετίζεται με πολλά Choice``s. Αυτό, βέβαια, γίνεται διότι η στήλη ``question του πίνακα Choice κρατάει το ID της στήλης ID του πίνακα Question. Έτσι, κάθε choice δεν μπορεί να αντιστοιχίσει σε παραπάνω από μια ερώτηση καθώς αυτό θα θεωρηθεί διπλή καταχώρηση (duplicate entry). Το Django υποστηρίζει όλου του είδους των κοινών συσχετίσεων μεταξύ βάσεων δεδομένων: many-to-one, many-to-many και one-to-one.

Ενεργοποιώντας τα μοντέλα

Αυτό το μικρό κομμάτι κώδικα δίνει στο Django πολλές πληροφορίες. Με αυτό, το Django μπορεί να:

  • Δημιουργήσει το database schema (CREATE TABLE statements) για αυτό το app.
  • Δημιουργήσει ένα Python database-access API για να έχετε πρόσβαση στα objects Question και Choice.

Αλλά πρώτα θα πρέπει να πούμε στο project μας ότι η εφαρμογή polls έχει εγκατασταθεί.

Φιλοσοφία

Τα apps στο Django είναι «επαναχρησιμοποιήσιμα»: Μπορείτε να χρησιμοποιήσετε το ίδιο app σε πολλά projects και επίσης μπορείτε να διανέμετε τα apps σας (να τα χρησιμοποιήσουν άλλοι προγραμματιστές), επειδή δεν είναι απαραίτητο να είναι συνδεδεμένα με κάποια συγκεκριμένη Django εγκατάσταση (περιβάλλον).

Για να συμπεριλάβουμε την εφαρμογή στο project μας, θα χρειαστεί να προσθέσουμε μια αναφορά (reference) στην κλάση παραμετροποίησης (configuration class) της εφαρμογής μέσα στη ρύθμιση-λίστα INSTALLED_APPS. Εφόσον η κλάση PollsConfig υπάρχει μέσα στο αρχείο polls/apps.py, το μονοπάτι για την αναφορά σε αυτή την κλάση είναι 'polls.apps.PollsConfig'. Σημειώστε ότι αναφερόμαστε σε αυτό χρησιμοποιώντας ένα string. Επεξεργαστείτε το γενικό αρχείο ρυθμίσεων του project σας, mysite/settings.py και προσθέστε το παραπάνω μονοπάτι (path) στη ρύθμιση-λίστα INSTALLED_APPS. Θα δείχνει κάπως έτσι:

mysite/settings.py
INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Τώρα το Django ξέρει ότι θα πρέπει να συμπεριλάβει, μεταξύ άλλων και την εφαρμογή με το όνομα polls. Ας τρέξουμε μια άλλη εντολή:

$ python manage.py makemigrations polls
...\> py manage.py makemigrations polls

Θα δείτε κάτι παρόμοιο με αυτό:

Migrations for 'polls':
  polls/migrations/0001_initial.py:
    - Create model Choice
    - Create model Question
    - Add field question to choice

Τρέχοντας την εντολή makemigrations, λέτε στο Django ότι έχετε κάνει κάποιες αλλαγές στο μοντέλο σας (σε αυτή την περίπτωση, έχετε δημιουργήσει καινούργια μοντέλα) και ότι θα θέλατε αυτές οι αλλαγές να αποθηκευτούν ως migration.

Τα migrations αναπαριστούν στο Django, τις αλλαγές στα μοντέλα σας (και συνεπώς στο schema της βάσης δεδομένων σας) - δεν είναι τίποτε άλλο παρά αρχεία (Python κώδικας) στον υπολογιστή σας. Μπορείτε, αν θέλετε, να διαβάσετε το migration για τα καινούργια μοντέλα που δημιουργήσαμε. Βρίσκεται στο αρχείο polls/migrations/0001_initial.py. Αλλά μην ανησυχείτε, δεν θα χρειαστεί να διαβάζετε αυτά τα αρχεία κάθε φορά που το Django τα δημιουργεί. Ωστόσο, είναι γραμμένα με φιλική-προς-τον-χρήστη μορφή ούτως ώστε να μπορείτε να τα επεξεργαστείτε στο μέλλον αν χρειαστεί.

Μέχρι τώρα, δηλαδή, έχουμε ορίσει τα μοντέλα μας, έχουμε δημιουργήσει τα migrations αλλά δεν έχει γραφτεί τίποτα στη βάση δεδομένων. Όπως καταλάβατε, για να δημιουργήσουμε τους πίνακες στη βάση (δημιουργία schema) δεν έχουμε παρά να τρέξουμε μια εντολή που θα τα τακτοποιήσει όλα αυτά για εμάς. Η εντολή αυτή λέγεται migrate και θα έρθουμε σε αυτή σε λίγο – αλλά πρώτα, ας ρίξουμε μια ματιά στην εντολή SQL που θα τρέξει στην ουσία. Η database δεν καταλαβαίνει Python κώδικα παρά μόνο SQL εντολές. Επομένως, για να δούμε την SQL εντολή που θα δοθεί (από το Django) στην βάση δεδομένων για τη δημιουργία των πινάκων (στην ουσία γίνεται μια μετάφραση του αρχείου polls/migrations/0001_initial.py σε SQL) δεν έχουμε παρά να τρέξουμε την Django εντολή sqlmigrate η οποία παίρνει ως όρισμα ονόματα migrations και επιστρέφει την μετάφραση τους σε SQL:

$ python manage.py sqlmigrate polls 0001
...\> py manage.py sqlmigrate polls 0001

Θα δείτε κάτι παρόμοιο με το ακόλουθο (έχουμε αλλοιώσει λίγο την έξοδο για λόγους αναγνωσιμότητας):

BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;

COMMIT;

Σημειώστε τα ακόλουθα:

  • Η ακριβής έξοδος της εντολής sqlmigrate θα διαφέρει αναλόγως την βάση δεδομένων που χρησιμοποιείτε. Το ανωτέρω παράδειγμα χρησιμοποίησε την PostgreSQL.
  • Table names are automatically generated by combining the name of the app (polls) and the lowercase name of the model – question and choice. (You can override this behavior.)
  • Τα primary keys (IDs) προστέθηκαν αυτόματα (μπορείτε να το παρακάμψετε και αυτό!).
  • Από προεπιλογή, το Django προσθέτει τη λέξη "_id" σε κάθε πεδίο foreign key (ω ναι, μπορείτε να το παρακάμψετε και αυτό!)
  • Η συσχέτιση του foreign key γίνεται κατηγορηματικά με τον FOREIGN KEY περιορισμό (constraint). Μην ανησυχείτε για τα πεδία DEFERRABLE. Αυτά λένε στην PostgreSQL να μην επιβάλλουν το foreign key μέχρις ότου ολοκληρωθεί η συναλλαγή (transaction).
  • Η εντολή αυτή (sqlmigrate) είναι βασισμένη στην βάση δεδομένων που χρησιμοποιείτε. Επομένως, συγκεκριμένα database πεδία όπως auto_increment (MySQL), serial (PostgreSQL) ή integer primary key autoincrement (SQLite) διαχειρίζονται από το Django για σας, αυτόματα. Το ίδιο ισχύει και για το quoting των field names – π.χ, αν χρησιμοποιείτε διπλά quotes (””) ή μονά (’’).
  • Η εντολή sqlmigrate δεν εκτελεί την SQL εντολή (δεν εκτελεί το migration, αν θέλετε) στην βάση δεδομένων σας – απλά το εκτυπώνει στην κονσόλα για να πάρετε μια γεύση του παραγόμενου κώδικα (από το Django). Είναι χρήσιμο όταν θέλετε να δείτε τι επρόκειτο να εκτελέσει το Django ή αν έχετε διαχειριστές βάσεων δεδομένων οι οποίοι χρειάζονται αυτά τα SQL scripts για τυχόν αλλαγές (προτού τις εκτελέσετε εσείς).

Αν ενδιαφέρεστε μπορείτε να τρέξετε την εντολή python manage.py check. Η εντολή αυτή ελέγχει όλο το project σας για τυχόν λάθη (συντακτικά, λογικά κλπ) δίχως να δημιουργεί migrations ή να αλληλεπιδρά με την database σας.

Τώρα μπορείτε να τρέξετε την εντολή migrate για να δημιουργήσετε τους πίνακες και τις συσχετίσεις μεταξύ τους στην βάση δεδομένων σας:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Rendering model states... DONE
  Applying polls.0001_initial... OK
...\> py manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Rendering model states... DONE
  Applying polls.0001_initial... OK

Η εντολή migrate λαμβάνει υπόψη της όλα τα migrations τα οποία δεν έχουν εφαρμοστεί (το Django γνωρίζει ποια migrations έχουν εφαρμοστεί χρησιμοποιώντας έναν ειδικό πίνακα στην βάση δεδομένων υπό το όνομα django_migrations) και τα τρέχει ένα-ένα - στη ουσία, πραγματοποιεί ένα είδους συγχρονισμό μεταξύ των αλλαγών των μοντέλων σας και του schema της βάσης σας.

Τα migrations είναι εξαιρετικά δυνατό χαρακτηριστικό του Django και σας επιτρέπει να αλλάζετε τα μοντέλα σας (όποτε το κρίνεται απαραίτητο) χωρίς να χρειαστεί να διαγράφετε κάθε φορά τη βάση ή τους πίνακες σας και να τους δημιουργείτε εκ νέου. Ειδικεύεται στο να εκσυγχρονίζει την βάση σας σε πραγματικό χρόνο χωρίς να χάνετε τα ήδη αποθηκευμένα δεδομένα σας. Θα καλύψουμε τα migrations, εκτενώς, σε επόμενο μέρος του οδηγού, αλλά προς το παρόν θυμηθείτε τα τρία βήματα που πρέπει να ακολουθήσετε για να αλλάζετε τα μοντέλα σας:

  • Αλλάξτε τα μοντέλα σας (στο αρχείο models.py).
  • Τρέξτε την εντολή python manage.py makemigrations για να δημιουργηθούν τα migrations για τις αλλαγές που κάνατε.
  • Τρέξτε την εντολή python manage.py migrate για να εκτελέσετε τις αλλαγές στη βάση δεδομένων.

The reason that there are separate commands to make and apply migrations is because you’ll commit migrations to your version control system and ship them with your app; they not only make your development easier, they’re also usable by other developers and in production.

Διαβάστε στο άρθρο εγχειρίδιο (documentation) του django-admin για περισσότερες πληροφορίες των δυνατοτήτων της εντολής manage.py. Εναλλακτικά μπορείτε να γράψετε στην κονσόλα την εντολή python manage.py για να ρίξετε μια γρήγορη ματιά στις διαθέσιμες εντολές.

Παίζοντας με το API

Ας μεταβούμε τώρα στην interactive Python κονσόλα (shell) και ας παίξουμε λίγο με το δωρεάν API το οποίο μας παρέχει το Django. Για να μεταβούμε στο Python shell, πληκτρολογήστε την εντολή (βεβαιωθείτε ότι βρίσκεστε στο root φάκελο του project σας, εκεί που βρίσκεται το αρχείο manage.py):

$ python manage.py shell
...\> py manage.py shell

Εκτελούμε δηλαδή με την Python το script manage.py το οποίο εκτελεί την εντολή shell. Δεν χρησιμοποιούμε σκέτο τη λέξη «python», επειδή το αρχείο manage.py θέτει την μεταβλητή περιβάλλοντος (environment variable) DJANGO_SETTINGS_MODULE, η οποία δίνει στο Django το Python import μονοπάτι για το αρχείο mysite/settings.py.

Όταν είστε μέσα στο shell, μπορείτε να εξερευνήσετε το database API:

>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

Wait a minute. <Question: Question object (1)> isn’t a helpful representation of this object. Let’s fix that by editing the Question model (in the polls/models.py file) and adding a __str__() method to both Question and Choice:

polls/models.py
from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

Είναι σημαντικό να προσθέσετε την μέθοδο __str__() στα μοντέλα σας, όχι μόνο για δική σας διευκόλυνση (προβολή μέσα από την κονσόλα) αλλά και γιατί το διαχειριστικό κομμάτι του Django (admin) χρησιμοποιεί ευρέως την μέθοδο αυτή (όπως θα δείτε αργότερα) για να σας παρουσιάσει τα ονόματα των entries της βάσης σας.

Σημειώστε ότι όλα αυτά είναι απλές μέθοδοι της Python. Ας προσθέσουμε μια δική μας μέθοδο, για λόγους επίδειξης:

polls/models.py
import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

Σημειώστε την προσθήκη του import datetime και from django.utils import timezone, ούτως ώστε να αναφερθούμε στο module της Python datetime και στο module του Django django.utils.timezone, αντίστοιχα. Αν δεν είστε εξοικειωμένοι με τον χειρισμό των ζωνών ώρας (time zone) στην Python, μπορείτε να μάθετε περισσότερα στο άρθρο time zone support docs.

Αποθηκεύστε τις αλλαγές στα μοντέλα σας (δεν χρειάζεται να τρέξετε τις εντολές makemigrations ή migrate γιατί δεν άλλαξε κάτι στα πεδία των μοντέλων σας) και ξεκινήστε εκ νέου ένα Python interactive shell τρέχοντας πάλι την εντολή python manage.py shell:

>>> from polls.models import Choice, Question

# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>

# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>

# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>

# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

Για περισσότερες πληροφορίες σχετικά με τις συσχετίσεις των μοντέλων, δείτε στο άρθρο Πρόσβαση συσχετισμένων objects. Για περισσότερες πληροφορίες σχετικά με το πώς να χρησιμοποιείτε τη διπλή κάτω παύλα για queries στην db μέσω του API, δείτε στο άρθρο Αναζητήσεις στα fields. Για περισσότερες λεπτομέρειες σχετικά με το database API, δείτε το άρθρο Αναφορά στο Database API.

Εισαγωγή στο Django Admin

Φιλοσοφία

Η δημιουργία των διαχειριστικών ιστοσελίδων (admin sites) για το προσωπικό σας ή τους πελάτες σας (ή ακόμη και για σας τον ίδιο) προκειμένου να προσθέσετε, αλλάξετε ή να διαγράψετε περιεχόμενο είναι μια ανιαρή διαδικασία η οποία δεν χρειάζεται και πολύ δημιουργικότητα. Για το λόγο αυτό το Django αυτοματοποιεί πλήρως την δημιουργία του διαχειριστικού interface για τα μοντέλα σας.

Το Django έχει γραφτεί σε ένα ειδησεογραφικό περιβάλλον, με πλήρη διαχωρισμό των εννοιών «εκδότες περιεχομένου» και «δημόσιο» site. Οι site managers χρησιμοποιούν το σύστημα για να εισάγουν νέα γεγονότα, αποτελέσματα αγώνων κλπ και αυτό το περιεχόμενο προβάλλεται στο «δημόσιο» site. Το Django λύνει το πρόβλημα της δημιουργίας ενός ενοποιημένου interface για τους site administrators με σκοπό την επεξεργασία του περιεχομένου.

Το admin site δεν είναι σχεδιασμένο για να χρησιμοποιηθεί από τους επισκέπτες της ιστοσελίδας σας. Είναι μόνο για αυτούς που θα διαχειρίζονται το site (site managers).

Δημιουργώντας έναν χρήστη admin

Πρώτα απ’όλα θα χρειαστεί να δημιουργήσουμε έναν χρήστη ο οποίος θα έχει πρόσβαση στο διαχειριστικό περιβάλλον (admin site). Τρέξτε την εντολή:

$ python manage.py createsuperuser
...\> py manage.py createsuperuser

Γράψτε το επιθυμητό όνομα χρήστη (username) και πιέστε enter.

Username: admin

Μετά θα πρέπει να γράψετε μια διεύθυνση email:

Email address: admin@example.com

Το τελευταίο βήμα είναι η επιλογή ενός συνθηματικού (password). Θα κληθείτε να γράψετε το συνθηματικό σας δύο φορές, (η δεύτερη φορά αποτελεί επιβεβαίωση της πρώτης).

Password: **********
Password (again): *********
Superuser created successfully.

Εκκίνηση του development server

Το Django admin site είναι ενεργό από προεπιλογή. Ας τρέξουμε τον development server και ας το εξερευνήσουμε.

Αν ο server δεν τρέχει, ξεκινήστε τον με την εντολή:

$ python manage.py runserver
...\> py manage.py runserver

Τώρα, ανοίξτε έναν Web browser και μεταβείτε στη σελίδα «/admin/» στο local domain σας – π.χ, http://127.0.0.1:8000/admin/. Θα πρέπει να δείτε την login οθόνη της Django admin σελίδας:

Django admin login screen

Επειδή η :doc:` μετάφραση </topics/i18n/translation>` είναι ενεργοποιημένη από προεπιλογή, πιθανόν η οθόνη εισόδου (login screen) να είναι στην δική σας γλώσσα, ανάλογα τις ρυθμίσεις γλώσσας του browser σας (θα πρέπει η Ελληνική γλώσσα να βρίσκεται πρώτη στη λίστα) και αν το Django υποστηρίζει την γλώσσα σας (σίγουρα υποστηρίζει την Ελληνική).

Επίσκεψη στο admin site

Προσπαθήστε τώρα να κάνετε login με τα credentials που δώσατε πριν. Θα πρέπει να δείτε την αρχική σελίδα του Django admin:

Django admin index page

Θα πρέπει να δείτε μερικούς τύπους περιεχομένου οι οποίοι μπορούν να επεξεργαστούν: ομάδες (groups) και χρήστες (users). Αυτοί παρέχονται από το authentication framework django.contrib.auth, το οποίο έρχεται προεγκατεστημένο με το Django.

Κάντε την εφαρμογή σας (poll) τροποποιήσιμη στο admin

Φαίνεται όμως να λείπει η εφαρμογή μας. Δεν εμφανίζεται καθόλου στο διαχειριστικό site.

Έχει μείνει μόνο ένα πράγμα να κάνουμε: θα πρέπει να πούμε στο admin ότι τα``Question`` objects θα έχουν admin interface. Για να το κάνετε αυτό, ανοίξτε το αρχείο polls/admin.py και επεξεργαστείτε το ούτως ώστε να δείχνει κάπως έτσι:

polls/admin.py
from django.contrib import admin

from .models import Question

admin.site.register(Question)

Εξερευνήστε την δωρεάν διαχειριστική λειτουργία

Τώρα που έχουμε κάνει register το μοντέλο Question στο admin, το Django ξέρει πως πρέπει να εμφανίσει στην αρχική σελίδα του admin, την εφαρμογή σας:

Django admin index page, now with polls displayed

Κλικάρετε στο «Questions». Τώρα είστε στη λεγόμενη «change list» σελίδα για τις ερωτήσεις. Αυτή η σελίδα εμφανίζει όλες τις ερωτήσεις (που είναι αποθηκευμένες στην βάση δεδομένων) και σας επιτρέπει να κλικάρετε πάνω τους και να τις επεξεργαστείτε. Εδώ φαίνεται και η ερώτηση που δημιουργήσαμε πριν («What’s up?»):

Polls change list page

Κλικάρετε Click the «What’s up?» question to edit it:

Editing form for question object

Μερικά πράγματα που αξίζει να σημειώσετε:

  • Η φόρμα δημιουργείται αυτόματα από το Question μοντέλο.
  • Τα διαφορετικού τύπου πεδία του μοντέλου Question (DateTimeField, CharField) αντιστοιχούν στα ανάλογα HTML input πεδία (widgets). Κάθε τύπος πεδίου γνωρίζει ποιο HTML input element να χρησιμοποιήσει στο Django admin (π.χ τα πεδία χαρακτήρων - CharField``s αντιστοιχούν στο ``<input type=”text” /> ενώ τα αριθμητικά ημερομηνία - IntegerField``s αντιστοιχούν στο ``<input type=”number” />).
  • Κάθε DateTimeField λαμβάνει ένα δωρεάν JavaScript plugin. Οι ημερομηνίες (dates) λαμβάνουν μια συντόμευση με το όνομα «Σήμερα» (όταν το κλικάρετε επιλέγεται η τωρινή ημερομηνία) καθώς και ένα ημερολόγιο (που εμφανίζεται σαν popup). Οι ώρες λαμβάνουν μια συντόμευση με το όνομα «Τώρα» (όταν το κλικάρετε επιλέγεται η τρέχουσα ώρα) και ένα βολικό popup το οποίο προβάλλει κοινότυπες ώρες προκειμένου να διαλέξετε αυτή που σας ταιριάζει.

Το κάτω μέρος της σελίδας (το οποίο με την ρύθμιση py, μπορεί να εμφανιστεί και στο πάνω μέρος) σας δίνει ορισμένες επιλογές:

  • Αποθήκευση – Αποθηκεύει τυχόν αλλαγές και σας επιστρέφει στην σελίδα change list για αυτό το object.
  • Αποθήκευση και συνέχεια επεξεργασίας – Αποθηκεύει τυχόν αλλαγές και επαναφορτώνει την ίδια σελίδα για αυτό το object.
  • Αποθήκευση και προσθήκη καινούργιου – Αποθηκεύει τυχόν αλλαγές και επαναφορτώνει την ίδια σελίδα, άδεια χωρίς τις τιμές του object που αποθηκεύτηκε προκειμένου να γίνει καταχώρηση νέου.
  • Διαγραφή – Σας ανακατευθύνει σε μια ενδιάμεση σελίδα επιβεβαίωσης της διαγραφής του object.

Αν η τιμή του «Date published» δεν ταιριάζει με την ώρα που αποθηκεύσατε την ερώτηση πριν (το Question object), αυτό σημαίνει ότι πιθανόν παραλείψατε να θέσετε τη σωστή τιμή στη ρύθμιση TIME_ZONE του αρχείου mysite/settings.py. Αλλάχτε το, επαναφορτώστε τη σελίδα και ελέγξτε αν εμφανίστηκε η σωστή τιμή.

Αλλάξτε τώρα την τιμή του «Date published» κλικάροντας στα κουμπιά «Σήμερα» και «Τώρα». Μετά κλικάρετε στο κουμπί » Αποθήκευση και συνέχεια επεξεργασίας». Μετά κλικάρετε στο κουμπί «Ιστορικό» (πάνω δεξιά της σελίδας). Θα δείτε μια σελίδα στην οποία εμφανίζονται όλες οι αλλαγές που έχουν γίνει μέχρι τώρα στο συγκεκριμένο object μέσω του Django admin, μαζί με ημερομηνία και ώρα που έγινε η αλλαγή καθώς και τον χρήστη που την έκανε:

History page for question object

Όταν νιώσετε άνετα με το API των μοντέλων (πειραματιστείτε με τις αναζητήσεις στην database, με τις συσχετίσεις των μοντέλων μεταξύ τους κλπ) και έχετε εξοικειωθεί με το διαχειριστικό περιβάλλον, συνεχίστε στο τρίτο μέρος αυτού του οδηγού για να μάθετε περισσότερα σχετικά με την προσθήκη views στην εφαρμογή μας (polls).

Back to Top