# Τεκμηρίωση του Django - 1.10.7

# Γράφοντας το πρώτο σας 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:

- `django.contrib.admin` – Το admin site. Θα φτάσετε εκεί σε λίγο.

- `django.contrib.auth` – Το σύστημα πιστοποίησης (authentication)

- `django.contrib.contenttypes` – Ένα framework για content types.

- `django.contrib.sessions` – Το session framework.

- `django.contrib.messages` – Το messaging framework.

- `django.contrib.staticfiles` – Ένα framework για να διαχειρίζεστε τα static files (css, js, φωτογραφίες, εικονίδια κλπ).

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

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

    $ python 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

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

    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

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

    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

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

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

- Αλλάξτε τα μοντέλα σας (στο αρχείο `models.py`).

- Τρέξτε την εντολή `python manage.py makemigrations` για να δημιουργηθούν τα migrations για τις αλλαγές που κάνατε.

- Τρέξτε την εντολή `python manage.py migrate` για να εκτελέσετε τις αλλαγές στη βάση δεδομένων.

Ο λόγος που υπάρχουν διαφορετικές εντολές για τη δημιουργία και την εκτέλεση των migrations είναι επειδή θα θέλετε να κάνετε commit τα migrations σε κάποιο version control system (π.χ git) και να διανείμετε τα migrations μαζί με την εφαρμογή σας. Αυτό, όχι μόνο κάνει την αναπτυξιακή σας πορεία (development) ευκολότερη αλλά επίσης τα migrations μπορούν να τα χρησιμοποιήσουν και άλλοι προγραμματιστές όπως επίσης μπορούν να χρησιμοποιηθούν και στην παραγωγή (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

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

Παρακάμπτοντας το manage.py

Αν δεν θέλετε να χρησιμοποιήσετε το αρχείο `manage.py`, κανένα πρόβλημα. Απλά θέστε την environment variable `DJANGO_SETTINGS_MODULE` μέσα στο αρχείο `mysite.settings` και ξεκινήστε ένα απλό Python shell (πληκτρολογώντας `python` στην κονσόλα) και σετάρετε το Django ως εξής:

    >>> import django
    >>> django.setup()

Αν αυτό κάνει raise το exception `AttributeError`, τότε πιθανόν να χρησιμοποιείτε κάποια έκδοση του Django που δεν είναι συμβατή με αυτό τον οδηγό. Θα πρέπει είτε να κοιτάξετε έναν παλαιότερο οδηγό (επιλογή στο κάτω μέρος δεξιά της σελίδας) είτε να αναβαθμίσετε την έκδοση του Django στην καινούργια έκδοση.

Θα πρέπει να τρέξετε την εντολή `python` από το ίδιο directory που βρίσκεται το αρχείο `manage.py`, ή να σιγουρευτείτε ότι το directory βρίσκεται μέσα στο Python path, προκειμένου η εντολή `import mysite` να δουλέψει.

Για περισσότερες πληροφορίες σχετικά με όλα αυτά δείτε το άρθρο εγχειρίδιο (documentation) του django-admin.

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

    >>> from polls.models import Question, Choice   # 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. Note that this might say "1L" instead of "1", depending
    # on which database you're using. That's no biggie; it just means your
    # database backend prefers to return integers as Python long integer
    # objects.
    >>> 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>]>

Για μισό λεπτό. Η εμφάνιση των ερωτήσεων ως `<Question: Question object>` δεν είναι και πολύ βοηθητική για ένα object. Ας το φτιάξουμε λίγο καλύτερα, επεξεργάζοντας το μοντέλο `Question` και `Choice` (μέσα στο αρχείο `polls/models.py`) προσθέτοντας τη μέθοδο `__str__()` και στα δύο μοντέλα:

polls/models.py

    from django.db import models
    from django.utils.encoding import python_2_unicode_compatible

    @python_2_unicode_compatible  # only if you need to support Python 2
    class Question(models.Model):
        # ...
        def __str__(self):
            return self.question_text

    @python_2_unicode_compatible  # only if you need to support Python 2
    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 Question, Choice

    # 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

Γράψτε το επιθυμητό όνομα χρήστη (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

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

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

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

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

Θα πρέπει να δείτε μερικούς τύπους περιεχομένου οι οποίοι μπορούν να επεξεργαστούν: ομάδες (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, την εφαρμογή σας:

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

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

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

- Η φόρμα δημιουργείται αυτόματα από το `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, μαζί με ημερομηνία και ώρα που έγινε η αλλαγή καθώς και τον χρήστη που την έκανε:

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