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

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

This tutorial begins where Tutorial 6 left off. We’re continuing the web-poll application and will focus on customizing Django’s automatically-generated admin site that we first explored in Tutorial 2.

Που να ψάξετε για βοήθεια

If you’re having trouble going through this tutorial, please head over to the Getting Help section of the FAQ.

## Παραμετροποιώντας την admin form

Κάνοντας register το `Question` model με το Django admin (`admin.site.register(Question)`), το Django ήταν σε θέση να κατασκευάσει μια προεπιλεγμένη εμφάνιση της φόρμας του μοντέλου για επεξεργασία. Με άλλα λόγια σας δίνει τη δυνατότητα να δημιουργείτε, επεξεργάζεστε, ανανεώνετε και διαγράφετε entries στη βάση δεδομένων σας (υπενθυμίζουμε ότι κάθε instance του μοντέλου σας αναπαριστά και μια γραμμή -entry, row- στον αντίστοιχο πίνακα της database) χωρίς τη χρήση της κονσόλας (γραμμής εντολών). Πολύ συχνά θα θέλετε να επεξεργαστείτε την εμφάνιση της φόρμας αυτής καθώς και τον τρόπο που λειτουργεί. Για να το πετύχετε αυτό δεν έχετε παρά να πείτε στο Django τις επιλογές-ρυθμίσεις που θέλετε, για αυτό το μοντέλο, την ώρα που κάνετε register το μοντέλο σας με το Django admin.

Ας δούμε πως δουλεύει αναδιοργανόνοντας τα πεδία (fields) στην φόρμα επεξεργασίας (edit form). Μέσα στο αρχείο `polls/admin.py`, αντικαταστήστε την γραμμή `admin.site.register(Question)` με αυτό το κομμάτι κώδικα:

`polls/admin.py`

    from django.contrib import admin

    from .models import Question

    class QuestionAdmin(admin.ModelAdmin):
        fields = ["pub_date", "question_text"]

    admin.site.register(Question, QuestionAdmin)

Θα ακολουθείτε αυτή τη διαδικασία (αρχικά θα δημιουργείτε μια model admin class και έπειτα θα περνάτε αυτή τη κλάση ως δεύτερο όρισμα στη μέθοδο `admin.site.register()`) κάθε φορά που θέλετε να αλλάξετε τυχόν επιλογές στο admin (admin options) για ένα μοντέλο.

Αν θυμάστε, πριν την αλλαγή αυτή, στη φόρμα επεξεργασίας εμφανιζόταν πρώτα το πεδίο της ερώτησης και μετά το πεδίο της ημερομηνίας επειδή τα είχαμε δηλώσει με αυτή τη σειρά στο μοντέλο `Question`. Τώρα, παρακάμπτουμε αυτή τη σειρά και πλέον εμφανίζεται πρώτα το πεδίο της ημερομηνίας («Publication date») και κατόπιν το πεδίο της ερώτησης («Question»):

Αυτό δεν είναι και τόσο εντυπωσιακό με μόλις δύο πεδία, αλλά για φόρμες admin (admin forms) με δεκάδες πεδία, η επιλογή της σειράς εμφάνισης αποτελεί σημαντική λεπτομέρεια.

Και μιλώντας για φόρμες με δεκάδες πεδία, ίσως να θέλετε να διαχωρίσετε τη φόρμα σε πεδία φορμών (fieldsets):

`polls/admin.py`

    from django.contrib import admin

    from .models import Question

    class QuestionAdmin(admin.ModelAdmin):
        fieldsets = [
            (None, {"fields": ["question_text"]}),
            ("Date information", {"fields": ["pub_date"]}),
        ]

    admin.site.register(Question, QuestionAdmin)

Το πρώτο element από κάθε tuple μέσα στη λίστα `fieldsets` είναι ο τίτλος του fieldset. Δείτε πως θα εμφανίζεται τώρα η φόρμα:

## Προσθήκη συσχετισμένων objects

Ωραία, έχουμε την σελίδα διαχείρισης του Question model, αλλά ένα `Question` έχει πολλαπλά `Choice`s και η φόρμα δεν εμφανίζει αυτές τις επιλογές.

Ακόμα.

There are two ways to solve this problem. The first is to register `Choice` with the admin just as we did with `Question`:

`polls/admin.py`

    from django.contrib import admin

    from .models import Choice, Question

    # ...
    admin.site.register(Choice)

Μετά από αυτό, το μοντέλο `Choice` απέκτησε τη δική του διαχειριστική φόρμα (admin form). Επομένως, τα πεδία της φόρμας θα εμφανιστούν με τη σειρά που δηλώθηκαν στο ίδιο το μοντέλο (εφόσον δεν κάναμε καμία αλλαγή μέσα στο `admin.py` όπως κάναμε για το `Question`). Η φόρμα θα δείχνει κάπως έτσι:

Σε αυτή τη φόρμα, το πεδίο «Question» αναπαριστάται από ένα HTML select box το οποίο περιέχει κάθε ερώτηση μέσα στη βάση δεδομένων. Το Django γνωρίζει ότι η κλάση `ForeignKey` θα πρέπει να αναπαρασταθεί ως ένα `<select>` box μέσα στην admin form. Στη δικιά μας περίπτωση, μόνο μια ερώτηση θα εμφανιστεί σε αυτό το σημείο (εκτός κι αν έχετε δημιουργήσει και άλλες).

Also note the «Add another question» link next to «Question.» Every object with a `ForeignKey` relationship to another gets this for free. When you click «Add another question», you’ll get a popup window with the «Add question» form. If you add a question in that window and click «Save», Django will save the question to the database and dynamically add it as the selected choice on the «Add choice» form you’re looking at.

Όμως αυτός ο τρόπος προσθήκης επιλογών (`Choice` objects) δεν είναι και ο πιο αποτελεσματικός που υπάρχει. Δε θα ήταν πιο βολικό αν δημιουργούσατε τα ``Choice``s κατά τη διάρκεια δημιουργίας των ερωτήσεων; Ας το υλοποιήσουμε.

Αφαιρέστε την κλήση της `register()` με το μοντέλο `Choice`. Έπειτα, επεξεργαστείτε τον registration κώδικα του `Question` για να γίνει έτσι:

`polls/admin.py`

    from django.contrib import admin

    from .models import Choice, Question

    class ChoiceInline(admin.StackedInline):
        model = Choice
        extra = 3

    class QuestionAdmin(admin.ModelAdmin):
        fieldsets = [
            (None, {"fields": ["question_text"]}),
            ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
        ]
        inlines = [ChoiceInline]

    admin.site.register(Question, QuestionAdmin)

Αυτό λέει στο Django: «Όλα τα `Choice` objects θα επεξεργάζονται στην διαχειριστική σελίδα του μοντέλου `Question`. Από προεπιλογή, να παρέχεις 3 choices».

Επισκεφτείτε, πάλι, τη σελίδα «Add question» για να δείτε πως φαίνεται:

Δουλεύει κάπως έτσι: Υπάρχουν τρία slots για τα συσχετισμένα Choices – όπως δηλώθηκε μέσω του attribute `extra` – και κάθε φορά που επισκέπτεστε τη σελίδα ενός ήδη υπάρχοντος object, παίρνετε άλλα τρία επιπλέον slots. Όταν όμως επισκέπτεστε για πρώτη φορά τη σελίδα (δημιουργία νέου object) θα λαμβάνετε τα πρώτα τρία slots.

At the end of the three current slots you will find an «Add another Choice» link. If you click on it, a new slot will be added. If you want to remove the added slot, you can click on the X to the top right of the added slot. This image shows an added slot:

One small problem, though. It takes a lot of screen space to display all the fields for entering related `Choice` objects. For that reason, Django offers a tabular way of displaying inline related objects. To use it, change the `ChoiceInline` declaration to read:

`polls/admin.py`

    class ChoiceInline(admin.TabularInline): ...

Με αυτό, `TabularInline` (αντί του `StackedInline`), τα συσχετισμένα objects εμφανίζονται πιο συμπυκνωμένα, σαν να βρίσκονται μέσα σε ένα πίνακα:

Note that there is an extra «Delete?» column that allows removing rows added using the «Add another Choice» button and rows that have already been saved.

## Παραμετροποιώντας την admin change list

Τώρα που η διαχειριστική σελίδα του μοντέλου Question δείχνει όμορφη, ας κάνουμε μερικές αλλαγές στην σελίδα «change list» – αυτή που εμφανίζει όλες τις ερωτήσεις μέσα στο σύστημα (σαν μια λίστα).

Δείτε πως φαίνεται σε αυτό το σημείο:

By default, Django displays the `str()` of each object. But sometimes it’d be more helpful if we could display individual fields. To do that, use the `list_display` admin option, which is a list of field names to display, as columns, on the change list page for the object:

`polls/admin.py`

    class QuestionAdmin(admin.ModelAdmin):
        # ...
        list_display = ["question_text", "pub_date"]

For good measure, let’s also include the `was_published_recently()` method from Tutorial 2:

`polls/admin.py`

    class QuestionAdmin(admin.ModelAdmin):
        # ...
        list_display = ["question_text", "pub_date", "was_published_recently"]

Τώρα η σελίδα change list του Question δείχνει κάπως έτσι:

Μπορείτε να κλικάρετε στους headers κάθε στήλης για να ταξινομήσετε την αντίστοιχη στήλη κατά τις τιμές της. Μοναδική εξαίρεση στη δυνατότητα της ταξινόμηση μιας στήλης αποτελεί η περίπτωση του header `was_published_recently`, επειδή η ταξινόμηση με βάση την έξοδο μιας μεθόδου (ενός μοντέλου) δεν υποστηρίζεται. Επίσης, σημειώστε ότι ο header της στήλης `was_published_recently` είναι, από προεπιλογή, το όνομα της μεθόδου (με τα κενά να αντικαθιστούν τις κάτω παύλες) και κάθε κελί της στήλης αυτής περιέχει την έξοδο της συγκεκριμένης μεθόδου (True ή False).

You can improve that by using the `display()` decorator on that method (extending the `polls/models.py` file that was created in Tutorial 2), as follows:

`polls/models.py`

    from django.contrib import admin

    class Question(models.Model):
        # ...
        @admin.display(
            boolean=True,
            ordering="pub_date",
            description="Published recently?",
        )
        def was_published_recently(self):
            now = timezone.now()
            return now - datetime.timedelta(days=1) <= self.pub_date <= now

For more information on the properties configurable via the decorator, see `list_display`.

Επεξεργαστείτε το αρχείο `polls/admin.py` για ακόμη μια φορά και προσθέστε ένα φίλτρο στη σελίδα change list του μοντέλου `Question` χρησιμοποιώντας το `list_filter`. Προσθέστε την ακόλουθη γραμμή κώδικα στην κλάση `QuestionAdmin`:

    list_filter = ["pub_date"]

Αυτό προσθέτει μια πλευρική στήλη (sidebar) «Filter» η οποία επιτρέπει το φιλτράρισμα των αποτελεσμάτων στην σελίδα change list κατά το πεδίο `pub_date`:

Ο τύπος του φίλτρου που εμφανίζεται εξαρτάται από τον τύπο του πεδίου που προσπαθείτε να φιλτράρετε. Επειδή το πεδίο `pub_date` είναι τύπου `DateTimeField`, το Django ξέρει ότι πρέπει να του δώσει τις ανάλογες επιλογές για φιλτράρισμα, όπως: «Οποιαδήποτε ημερομηνία», «Σήμερα», «Τελευταίες 7 μέρες», «Αυτόν το μήνα», «Αυτόν το χρόνο».

Αρχίζει και παίρνει σχήμα η όλη σελίδα μας. Ας προσθέσουμε τη δυνατότητα του searching:

    search_fields = ["question_text"]

Αυτό προσθέτει ένα search box κοντά στην κορυφή της σελίδας change list. Όταν κάποιος πληκτρολογήσει κάποια λέξη κλειδί για εύρεση, το Django θα ψάξει μέσα στο πεδίο `question_text`. Μπορείτε να χρησιμοποιήσετε όσα πεδία θέλετε – όμως επειδή η μέθοδος αυτή κάνει χρήση του `LIKE` query στο παρασκήνιο, αν περιορίσετε τον αριθμό των search fields μέσα στο πεδίο `search_fields`, τότε θα κάνει τη δουλειά της database σας πιο εύκολη στην εύρεση της λέξης κλειδί που δώσατε.

Τώρα είναι μια καλή στιγμή να σημειώσουμε ότι η σελίδες change lists σας δίνουν δωρεάν τη δυνατότητα του pagination. Από προεπιλογή, το Djoango εμφανίζει 100 γραμμές ανά σελίδα. Τα attributes `Change list pagination`, `search boxes`, `filters`, `date-hierarchies` και `column-header-ordering` συνεργάζονται μεταξύ τους όπως ακριβώς νομίζετε.

## Παραμετροποιώντας την εμφάνιση του admin site

Βασικά, η λέξη «Διαχείριση Django» στην κορυφή κάθε σελίδας του admin είναι γελοία. Στην πραγματικότητα είναι ένα κείμενο placeholder.

You can change it, though, using Django’s template system. The Django admin is powered by Django itself, and its interfaces use Django’s own template system.

### Παραμετροποιώντας τα templates του project σας

Create a `templates` directory in your `djangotutorial` directory. Templates can live anywhere on your filesystem that Django can access. (Django runs as whatever user your server runs.) However, keeping your templates within the project is a good convention to follow.

Ανοίξτε το αρχείο ρυθμίσεων-επιλογών του project σας (settings file, `mysite/settings.py`) και προσθέστε την επιλογή `DIRS` μέσα στην επιλογή `TEMPLATES`:

`mysite/settings.py`

    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "DIRS": [BASE_DIR / "templates"],
            "APP_DIRS": True,
            "OPTIONS": {
                "context_processors": [
                    "django.template.context_processors.request",
                    "django.contrib.auth.context_processors.auth",
                    "django.contrib.messages.context_processors.messages",
                ],
            },
        },
    ]

Η επιλογή `DIRS` είναι μια λίστα με strings. Κάθε string είναι η διαδρομή (path) η οποία καταλήγει σε κάποιο φάκελο που περιέχει τα templates σας. Όταν το Django φορτώνει τα templates θα ψάξει και σε αυτά τα paths για να βρει τα templates σας. Με άλλα λόγια αποτελεί ένα μονοπάτι ευρέσεως των templates.

Οργανώνοντας τα templates

Just like the static files, we could have all our templates together, in one big templates directory, and it would work perfectly well. However, templates that belong to a particular application should be placed in that application’s template directory (e.g. `polls/templates`) rather than the project’s (`templates`). We’ll discuss in more detail in the reusable apps tutorial why we do this.

Now create a directory called `admin` inside `templates`, and copy the template `admin/base_site.html` from within the default Django admin template directory in the source code of Django itself (django/contrib/admin/templates) into that directory.

Που είναι ο πηγαίος κώδικας του Django?

Αν δυσκολεύεστε να βρείτε τον πηγαίο κώδικα του Django μέσα στο σύστημα σας, τρέξτε την ακόλουθη εντολή:

/ 

    $ python -c "import django; print(django.__path__)"

    ...\> py -c "import django; print(django.__path__)"

Then, edit the file and replace `{{ site_header|default:_('Django administration') }}` (including the curly braces) with your own site’s name as you see fit. You should end up with a section of code like:

    {% block branding %}
    <div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></div>
    {% if user.is_anonymous %}
      {% include "admin/color_theme_toggle.html" %}
    {% endif %}
    {% endblock %}

Χρησιμοποιούμε αυτή τη διαδικασία για να σας δείξουμε πως μπορείτε να κάνετε override τα templates. Σε ένα πραγματικό project, το πιο πιθανόν είναι να χρησιμοποιήσετε το attibute `django.contrib.admin.AdminSite.site_header` κάνοντας την παραπάνω διαδικασία ακόμα πιο εύκολη.

Αυτό το αρχείο template περιέχει αρκετό κείμενο όπως `{% block branding %}` και `{{ title }}`. Τα tags `{%` και `{{` είναι μέρος της γλώσσας template του Django. Όταν το Django κάνει render το αρχείο `admin/base_site.html`, αυτή η γλώσσα θα τρέξει και θα γεμίσει αυτά τα tags με διάφορες τιμές για να παραχθεί στο τέλος η HTML σελίδα που θα επιστρέψει πίσω στον χρήστη (ως response), όπως είδαμε στον Οδηγό 3.

Note that any of Django’s default admin templates can be overridden. To override a template, do the same thing you did with `base_site.html` – copy it from the default directory into your custom directory, and make changes.

### Παραμετροποιώντας τα templates της εφαρμογής σας

Οι προσεκτικοί αναγνώστες θα αναρρωτηθούν: Αφού η ρύθμιση (λίστα) `DIRS` είναι κενή αρχικά, πως ξέρει το Django που να βρει τα προεπιλεγμένα admin templates; Η απάντηση έχει ως εξής: εφόσον η ρύθμιση (boolean) `APP_DIRS` έχει την τιμή `True`, το Django αυτόματα κοιτάζει στο φάκελο `templates/` κάθε εφαρμογής (που συμπεριλαμβάνεται στη ρύθμιση-λίστα `INSTALLED_APPS`), για τη χρήση τους ως fallback (μην ξεχνάτε ότι το `django.contrib.admin` είναι και αυτό μια εφαρμογή).

Η εφαρμογή ψηφοφορίας μας δεν είναι πολύ περίπλοκη και δεν χρειάζεται κάποια μεγάλη παραμετροποίηση των admin templates. Αλλά αν αναπτυχθεί και μεγαλώσει σε τέτοιο βαθμό που να χρειάζεται απαραιτήτως διάφορες λειτουργικές επεμβάσεις στα default templates του Django, τότε θα ήταν πιο λογικό να γίνει σε επίπεδο application (μέσα στο φάκελο templates της εφαρμογής σας) παρά σε επίπεδο project (μέσα στο φάκελο templates του project σας). Με αυτό τον τρόπο, θα μπορέσετε να συμπεριλάβετε την εφαρμογή ψηφοφορίας σας σε κάποιο μελλοντικό project και θα είστε σίγουροι ότι τα templates αυτής της εφαρμογής θα εφαρμοστούν αμέσως στο νέο project.

Δείτε στο εγχειρίδιο (documentation) φόρτωσης των templates για περισσότερες πληροφορίες σχετικά με το πως το Django βρίσκει-αναζητά τα templates.

## Παραμετροποιώντας την αρχική σελίδα του Django admin

Ομοίως, ίσως να θέλετε να παραμετροποιήσετε (να φέρετε στα μέτρα σας) την εμφάνιση της αρχικής σελίδας του Django admin.

Από προεπιλογή, εμφανίζονται όλες οι εφαρμογές (κατά αλφαβητική σειρά) που συμπεριλαμβάνονται στη ρύθμιση (λίστα) `INSTALLED_APPS` και έχουν γίνει register με το admin. Αν θέλετε να κάνετε σημαντικές αλλαγές στην εμφάνιση της αρχικής σελίδας δεν έχετε παρά να βάλετε τη φαντασία σας να δουλέψει. Είναι γνωστό ότι η εμφάνιση της αρχικής σελίδας του admin είναι η πιο σημαντική και θα πρέπει να είναι εύκολο να την παραμετροποιήσετε.

The template to customize is `admin/index.html`. (Do the same as with `admin/base_site.html` in the previous section – copy it from the default directory to your custom template directory). Edit the file, and you’ll see it uses a template variable called `app_list`. That variable contains every installed Django app. Instead of using that, you can hardcode links to object-specific admin pages in whatever way you think is best.

When you’re comfortable with the admin, read part 8 of this tutorial to learn how to use third-party packages.