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

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

This tutorial begins where Tutorial 2 left off. We’re continuing the web-poll application and will focus on creating the public interface – «views.»

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

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

## Επισκόπηση

A view is a «type» of web page in your Django application that generally serves a specific function and has a specific template. For example, in a blog application, you might have the following views:

- Blog homepage – προβάλει τα μερικά από τα τελευταία entries (καταχωρήσεις).

- Σελίδα «λεπτομερειών» μιας entry (καταχώρησης) – μια permalink σελίδα για ένα συγκεκριμένο entry.

- Σελίδα archive ανά ημερολογιακό χρόνο – προβάλει όλους τους μήνες που έχουν καταχωρήσεις για το δοθέντα έτος.

- Σελίδα archive ανά ημερολογιακό μήνα – προβάλει όλες τις μέρες που έχουν καταχωρήσεις για το δοθέντα μήνα.

- Σελίδα archive ανά ημερολογιακή μέρα – προβάλει όλα τα entries μια συγκεκριμένη μέρα.

- Προσθήκη σχολίου – χειρίζεται τα σχόλια πάνω στα posts για ένα συγκεκριμένο entry.

Στη δική μας εφαρμογή, θα έχουμε τα ακόλουθα τέσσερα views:

- Αρχική σελίδα («index») ερωτήσεων – προβάλει τις τελευταίες ερωτήσεις.

- Σελίδα «λεπτομερειών» μιας ερώτησης – προβάλει μια ερώτηση, χωρίς αποτελέσματα, αλλά με μια φόρμα (η οποία θα περιέχει τις επιλογές της ερώτησης) προς ψήφιση.

- Σελίδα «αποτελεσμάτων» μιας ερώτησης – προβάλει τα αποτελέσματα μιας συγκεκριμένης ερώτησης.

- Προσθήκη ψήφου – χειρίζεται τη ψήφο μιας συγκεκριμένης επιλογής μιας συγκεκριμένης ερώτησης.

In Django, web pages and other content are delivered by views. Each view is represented by a Python function (or method, in the case of class-based views). Django will choose a view by examining the URL that’s requested (to be precise, the part of the URL after the domain name).

Now in your time on the web you may have come across such beauties as `ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B`. You will be pleased to know that Django allows us much more elegant URL patterns than that.

A URL pattern is the general form of a URL - for example: `/newsarchive/<year>/<month>/`.

To get from a URL to a view, Django uses what are known as “URLconfs”. A URLconf maps URL patterns to views.

This tutorial provides basic instruction in the use of URLconfs, and you can refer to URL dispatcher for more information.

## Γράφοντας περισσότερα views

Ας προσθέσουμε τώρα μερικά views παραπάνω, στο αρχείο `polls/views.py`. Αυτά τα views είναι ελαφρώς διαφορετικά, επειδή παίρνουν και όρισμα (argument):

`polls/views.py`

    def detail(request, question_id):
        return HttpResponse("You're looking at question %s." % question_id)

    def results(request, question_id):
        response = "You're looking at the results of question %s."
        return HttpResponse(response % question_id)

    def vote(request, question_id):
        return HttpResponse("You're voting on question %s." % question_id)

Wire these new views into the `polls.urls` module by adding the following `path()` calls:

`polls/urls.py`

    from django.urls import path

    from . import views

    urlpatterns = [
        # ex: /polls/
        path("", views.index, name="index"),
        # ex: /polls/5/
        path("<int:question_id>/", views.detail, name="detail"),
        # ex: /polls/5/results/
        path("<int:question_id>/results/", views.results, name="results"),
        # ex: /polls/5/vote/
        path("<int:question_id>/vote/", views.vote, name="vote"),
    ]

Take a look in your browser, at «/polls/34/». It’ll run the `detail()` function and display whatever ID you provide in the URL. Try «/polls/34/results/» and «/polls/34/vote/» too – these will display the placeholder results and voting pages.

When somebody requests a page from your website – say, «/polls/34/», Django will load the `mysite.urls` Python module because it’s pointed to by the `ROOT_URLCONF` setting. It finds the variable named `urlpatterns` and traverses the patterns in order. After finding the match at `'polls/'`, it strips off the matching text (`"polls/"`) and sends the remaining text – `"34/"` – to the “polls.urls” URLconf for further processing. There it matches `'<int:question_id>/'`, resulting in a call to the `detail()` view like so:

    detail(request=<HttpRequest object>, question_id=34)

The `question_id=34` part comes from `<int:question_id>`. Using angle brackets «captures» part of the URL and sends it as a keyword argument to the view function. The `question_id` part of the string defines the name that will be used to identify the matched pattern, and the `int` part is a converter that determines what patterns should match this part of the URL path. The colon (`:`) separates the converter and pattern name.

## Γράφοντας views οι οποίες κάνουν κάτι

Κάθε view είναι υπεύθυνο στο να κάνει ένα από δύο πράγματα: να επιστρέφει ένα `HttpResponse` object το οποίο περιέχει το περιεχόμενο για την σελίδα που ζητήθηκε ή να κάνει raise κάποιο exception όπως το `Http404`. Τα υπόλοιπα εξαρτώνται από εσάς.

Το view σας μπορεί να διαβάσει records από την βάση δεδομένων ή όχι. Μπορεί να χρησιμοποιήσει ένα template system όπως αυτό του Django – ή κάποιο τρίτο Python template system – ή όχι. Μπορεί να παράγει κάποιο PDF αρχείο, να εξάγει XML, να δημιουργεί κάποιο ZIP αρχείο, να εξάγει JSON δεδομένα, βασικά μπορεί να κάνει τα πάντα χρησιμοποιώντας οποιεσδήποτε βιβλιοθήκες Python επιθυμείτε.

Το μόνο που απαιτείται από το Django είναι ένα `HttpResponse` ή μια exception.

Επειδή είναι βολικό, θα χρησιμοποιήσουμε το database API του Django, το οποίο καλύψαμε στον Οδηγό 2. Παρακάτω βλέπετε ένα καινούργιο view με το όνομα `index()`, το οποίο εμφανίζει τις τελευταίες 5 ερωτήσεις (που υπάρχουν στην βάση δεδομένων), διαχωρισμένες με κόμμα, βάση της ημερομηνίας που καταχωρήθηκαν (δημιουργήθηκαν):

`polls/views.py`

    from django.http import HttpResponse

    from .models import Question

    def index(request):
        latest_question_list = Question.objects.order_by("-pub_date")[:5]
        output = ", ".join([q.question_text for q in latest_question_list])
        return HttpResponse(output)

    # Leave the rest of the views (detail, results, vote) unchanged

There’s a problem here, though: the page’s design is hardcoded in the view. If you want to change the way the page looks, you’ll have to edit this Python code. So let’s use Django’s template system to separate the design from Python by creating a template that the view can use.

Για αρχή, δημιουργήστε ένα φάκελο με το όνομα `templates` κάτω από τον φάκελο `polls`. Το Django θα κοιτάξει για templates εκεί.

Η ρύθμιση `TEMPLATES` του project σας περιγράφει πως το Django θα φορτώνει και θα κάνει render τα templates. Η προεπιλεγμένη ρύθμιση στο αρχείο settings παραμετροποιεί το `DjangoTemplates` backend του οποίου η ρύθμιση `APP_DIRS` είναι ρυθμισμένη στο `True`. Έτσι, το `DjangoTemplates` ψάχνει για ένα υπό-φάκελο «templates» σε κάθε εφαρμογή (app) που είναι περασμένη στη ρύθμιση `INSTALLED_APPS`.

Within the `templates` directory you have just created, create another directory called `polls`, and within that create a file called `index.html`. In other words, your template should be at `polls/templates/polls/index.html`. Because of how the `app_directories` template loader works as described above, you can refer to this template within Django as `polls/index.html`.

Template namespacing

Now we might be able to get away with putting our templates directly in `polls/templates` (rather than creating another `polls` subdirectory), but it would actually be a bad idea. Django will choose the first template it finds whose name matches, and if you had a template with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the best way to ensure this is by namespacing them. That is, by putting those templates inside another directory named for the application itself.

Βάλτε τον ακόλουθο κώδικα στο template:

`polls/templates/polls/index.html`

    {% if latest_question_list %}
        <ul>
        {% for question in latest_question_list %}
            <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No polls are available.</p>
    {% endif %}

Σημείωση

To make the tutorial shorter, all template examples use incomplete HTML. In your own projects you should use complete HTML documents.

Τώρα, ας ανανεώσουμε το `index` view μέσα στο αρχείο `polls/views.py` ούτως ώστε να χρησιμοποιήσει το νέο μας template:

`polls/views.py`

    from django.http import HttpResponse
    from django.template import loader

    from .models import Question

    def index(request):
        latest_question_list = Question.objects.order_by("-pub_date")[:5]
        template = loader.get_template("polls/index.html")
        context = {"latest_question_list": latest_question_list}
        return HttpResponse(template.render(context, request))

Αυτός ο κώδικας φορτώνει το template με το όνομα `polls/index.html` και περνάει σε αυτό ένα context. Το context είναι ένα dictionary το οποίο αντιστοιχεί template variable ονόματα σε Python objects.

Πηγαίνετε στη σελίδα με το browser σας στο «/polls/» και θα πρέπει να δείτε μια λίστα (με κουκίδες) η οποία περιέχει την ερώτηση «What’s up» που φτιάξαμε στον Οδηγό 2. Το link δείχνει στην σελίδα με τις λεπτομέρειες της ερώτησης.

### Η συντόμευση: `render()`

Είναι πολύ συνηθισμένο να φορτώνεται ένα template, να γεμίζεται ένα context και να επιστρέφεται ένα `HttpResponse` object του οποίου το αποτέλεσμα θα είναι το rendered template. Το Django παρέχει μια συντόμευση για όλα αυτά. Ορίστε πάλι το `index()` view, γραμμένο από την αρχή:

`polls/views.py`

    from django.shortcuts import render

    from .models import Question

    def index(request):
        latest_question_list = Question.objects.order_by("-pub_date")[:5]
        context = {"latest_question_list": latest_question_list}
        return render(request, "polls/index.html", context)

Σημειώστε ότι, αφού χρησιμοποιήσουμε την `render()` για όλες τις views, δεν χρειάζεται πλέον να κάνουμε import τα `loader` και `HttpResponse`. Παραταύτα, θα χρειαστεί να κρατήσετε το `HttpResponse` αν έχετε ακόμη τις μεθόδους για το``detail``, `results` και `vote` view.

Η συνάρτηση `render()` παίρνει ως πρώτο όρισμα το request object, ως δεύτερο το όνομα του template και ένα dictionary ως τρίτο προαιρετικό. Επιστρέφει ένα `HttpResponse` object του δηλωθέν template το οποίο template έχει γίνει rendered βάση του δοθέν context.

## Raising σφάλμα 404

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

`polls/views.py`

    from django.http import Http404
    from django.shortcuts import render

    from .models import Question

    # ...
    def detail(request, question_id):
        try:
            question = Question.objects.get(pk=question_id)
        except Question.DoesNotExist:
            raise Http404("Question does not exist")
        return render(request, "polls/detail.html", {"question": question})

Το νέο concept εδώ είναι: Το view κάνει raise την exception `Http404` αν η ερώτηση με το συγκεκριμένο ID δεν υπάρχει (στη βάση δεδομένων).

Θα συζητήσουμε τι μπορείτε να βάλετε μέσα στο template με το όνομα `polls/detail.html` λίγο αργότερα, αλλά τώρα αν βιάζεστε να δείτε πως θα φαίνεται γράψτε τα ακόλουθα στο αρχείο `polls/detail.html`:

`polls/templates/polls/detail.html`

    {{ question }}

Αυτό θα σας δώσει μια ιδέα.

### Η συντόμευση: `get_object_or_404()`

Είναι συνηθισμένο να χρησιμοποιείται η μέθοδος `get()` η οποία να κάνει raise το exception `Http404` αν το object δεν υπάρχει. Το Django σας παρέχει μια συντόμευση και γι’ αυτό. Παρακάτω φαίνεται το `detail()` view, γραμμένο από την αρχή (πάλι):

`polls/views.py`

    from django.shortcuts import get_object_or_404, render

    from .models import Question

    # ...
    def detail(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        return render(request, "polls/detail.html", {"question": question})

Η συνάρτηση `get_object_or_404()` παίρνει ως πρώτο όρισμα ένα Django model και ένα αυθαίρετο αριθμό από arguments, τα οποία τα δίνει στην μέθοδο `get()` του manager του μοντέλου (model manager) που περάστηκε ως πρώτο όρισμα. Η συνάρτηση αυτή κάνει raise το exception `Http404` αν το object δεν υπάρχει.

Φιλοσοφία

Γιατί όμως χρησιμοποιούμε την βοηθητική συνάρτηση (helper function) `get_object_or_404()` αντί να κάνουμε αυτόματα το catch του exception `ObjectDoesNotExist` σε ένα υψηλότερο επίπεδο; Επίσης, γιατί να βάλουμε το model API να κάνει raise το exception `Http404` αντί του exception `ObjectDoesNotExist`;

Η απάντηση είναι ίδια και στις δύο ερωτήσεις. Επειδή αυτό θα δεσμεύσει το επίπεδο του model με το επίπεδο του view. Ένας από τους πρωταρχικούς στόχους του Django είναι να διατηρηθεί η ανεξαρτητοποίηση και η μη σύνδεση μεταξύ οντοτήτων (το λεγόμενο loose coupling). Μερικά ελεγχόμενα couplings παρουσιάζονται στο module `django.shortcuts`.

Υπάρχει επίσης η συνάρτηση `get_list_or_404()`, η οποία λειτουργεί πανομοιότυπα με την `get_object_or_404()` – με τη μόνη διαφορά ότι χρησιμοποιεί την μέθοδο `filter()` αντί της `get()`. Η `get_list_or_404()` κάνει raise την exception `Http404` αν η λίστα είναι κενή (άδεια).

## Χρησιμοποιώντας ένα template system

Πίσω στο `detail()` view της εφαρμογής μας. Έχοντας το context variable με το όνομα `question`, το template `polls/detail.html` θα δείχνει κάπως έτσι:

`polls/templates/polls/detail.html`

    <h1>{{ question.question_text }}</h1>
    <ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}</li>
    {% endfor %}
    </ul>

Το template system χρησιμοποιεί την σύνταξη της τελείας για να ψάχνει μέσα στα variable attributes. Στο παράδειγμα του `{{ question.question_text }}`, το Django θα ψάξει πρώτα μέσα στο dictionary του object `question`. Αν δεν βρει τίποτα (δεν υπάρχει, δηλαδή, το key `question_text``στο υποτιθέμενο dictionary), προσπαθεί να βρει το ``question_text` σαν attribute του object – το οποίο στη συγκεκριμένη περίπτωση ισχύει. Αν όμως δεν υπήρχε, τότε θα έψαχνε σε μια λίστα να το βρει.

Ο τρόπος για να καλείτε μεθόδους φαίνεται στο βρόγχο επανάληψης `{% for %}`: το `question.choice_set.all` μεταφράζεται ως Python κώδικας `question.choice_set.all()`, το οποίο επιστρέφει ένα iterable από `Choice` objects και είναι βολικό προς χρήση (στην παρούσα) με τον βρόγχο επανάληψης `{% for %}` (το οποίο αναφέρεται και ως `template tag`).

Δείτε το άρθρο templates για περισσότερα σχετικά με τα templates.

## Αφαίρεση των hardcoded URLs στα templates

Θυμηθείτε ότι πριν, δηλώσαμε στην HTML το link για την ερώτηση στο template `polls/index.html` ως:

    <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

The problem with this hardcoded, tightly-coupled approach is that it becomes challenging to change URLs on projects with a lot of templates. However, since you defined the `name` argument in the `path()` functions in the `polls.urls` module, you can remove a reliance on specific URL paths defined in your url configurations by using the `{% url %}` template tag:

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

Ο τρόπος με τον οποίο λειτουργεί αυτό το template tag είναι κοιτώντας τα URLs που έχετε ορίσει στο module `polls.urls`. Μπορείτε να δείτε ακριβώς σε ποιο URL έχει οριστεί το όνομα “detail” παρακάτω:

    ...
    # the 'name' value as called by the {% url %} template tag
    path("<int:question_id>/", views.detail, name="detail"),
    ...

Αν θέλετε να αλλάξετε το URL αυτού του view σε κάτι άλλο (ίσως στο `polls/specifics/12/`), τότε αντί να ψάχνετε όλα τα templates που είχατε δηλώσει το παλιό URL προκειμένου να το αντικαταστήσετε με το καινούργιο, πολύ απλά πειράζετε ένα αρχείο (`polls/urls.py`) και όλα τα υπόλοιπα ανανεώνονται αυτόματα, χάρη στο template tag `{% url %}`:

    ...
    # added the word 'specifics'
    path("specifics/<int:question_id>/", views.detail, name="detail"),
    ...

## Χρησιμοποιώντας namespacing στα ονόματα των URL

Το project του οδηγού αυτού έχει μόνο μια εφαρμογή, την `polls`. Σε πραγματικά Django projects, ίσως να έχετε πέντε, δέκα, είκοσι ή περισσότερα apps. Πως μπορεί να ξέρει και να διαφοροποιεί το Django τα ονόματα των URLs ανάμεσα σε τόσα apps; Για παράδειγμα, η εφαρμογή `polls` έχει ένα `detail` view και ίσως ένα άλλο app (στο ίδιο project) να έχει το ίδιο όνομα για ένα δικό του view. Όταν το Django συναντήσει το template tag `{% url 'detail' %}` πως θα ξέρει σε ποιο view να αναφερθεί;

Η απάντηση έρχεται με την προσθήκη namespaces στο URLconf. Στο αρχείο `polls/urls.py`, προσθέστε μια μεταβλητή `app_name` ούτως ώστε να θέσετε το namespace για αυτό το app:

`polls/urls.py`

    from django.urls import path

    from . import views

    app_name = "polls"
    urlpatterns = [
        path("", views.index, name="index"),
        path("<int:question_id>/", views.detail, name="detail"),
        path("<int:question_id>/results/", views.results, name="results"),
        path("<int:question_id>/vote/", views.vote, name="vote"),
    ]

Τώρα, πηγαίνετε και αλλάξτε το αρχείο ``polls/index.html``από:

`polls/templates/polls/index.html`

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

στο:

`polls/templates/polls/index.html`

    <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

When you’re comfortable with writing views, read part 4 of this tutorial to learn the basics about form processing and generic views.