\d+)/vote/$', views.vote, name='vote'),
)
```
---
## Pogled koji nešto zaista i radi
Fajl `polls/views.py`:
```python
from django.http import HttpResponse
from polls.models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([p.question_text for p in latest_question_list])
return HttpResponse(output)
# Leave the rest of the views (detail, results, vote) unchanged
```
- Problem je što je izled vraćene strane hardkodiran u *view* funkciji.
- Prepustićemo renderovanje stranice Django obrađivaču šablona.
---
## Šabloni
- Kreiramo folder `templates` unutar `polls` aplikacije.
- Django koristi n-torku `TEMPLATE_LOADERS` za *callables* koji "znaju" kako da
učitaju šablon iz raličitih izvora.
- Jedan od loadera je `django.template.loaders.app_directories.Loader` koji
učitava iz `templates` foldera aplikacije.
- U `templates` folderu kreiramo `polls` direktorijum i u njemu fajl
`index.html`.
- Putanja je dakle `polls/templates/polls/index.html`
- Iz aplikacije šablon se (zahvaljujući loaderu) referencira sa
`polls/index.html`
---
## Prvi šablon
Fajl `polls/templates/polls/index.html`:
```django
{% if latest_question_list %}
{% else %}
No polls are available.
{% endif %}
```
---
## Ažuriranje pogleda da koristi šablon
```python
from django.http import HttpResponse
from django.template import RequestContext, loader
from polls.models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = RequestContext(request, {
'latest_question_list': latest_question_list,
})
return HttpResponse(template.render(context))
```
---
## Prečica `render`
- Učitavanje i rederovanje šablona i vraćanje `HttpResponse` instance
je čest slučaj.
- Zbog toga postoji funkcija koja obavlja sav taj posao - `render()`
```python
from django.shortcuts import render
from polls.models import Question
def index(request):
latest_question_list = Question.objects.all().order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
```
---
## Greška 404
Recimo da želimo da renderujemo detalje `Question` objekta.
```python
from django.http import Http404
from django.shortcuts import render
from polls.models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404
return render(request, 'polls/detail.html', {'question': question})
```
Za sada šablon `polls/detail.html` može biti prosto:
```django
{{ question }}
```
---
## Prečica `get_object_or_404`
- Čest obrazac je pronalaženje objekta po id-u i podizanje greške 404 ukoliko ne
postoji.
- Za to može da se upotrebi prečica `get_object_or_404()`.
```python
from django.shortcuts import get_object_or_404, render
from polls.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})
```
- Takođe postoji i `get_list_or_404()` koja koristi `filter` i podiže grešku 404
ukoliko je lista prazna.
---
## Prepravka details
šablona
Fajl `polls/templates/polls/detail.html`:
```django
{{ question.question_text }}
{% for choice in question.choice_set.all %}
- {{ choice.choice_text }}
{% endfor %}
```
---
## URL-ovi u šablonima
- Link u `index.html` šablonu je bio delimično hardkodiran:
```django
{{ question.question_text}}
```
- To nije dobro kod većih aplikacija jer otežava promenu URL šeme.
- Zbog toga je bolje koristiti `{% url %}` tag.
```django
{{ question.question_text }}
Fajl polls/urls.py
...
```
```python
# the 'name' value as called by the {% url %} template tag
url(r'^(?P\d+)/$', views.detail, name='detail'),
...
```
---
## *Namespaces* u URL rutama
.medium[
- U prethodnom primeru `url` tag referencira rutu iz `urls.py` fajla po imenu.
- Problem je ako imamo rute koje se isto zovu u više aplikacija.
- To se rešava domenom imena (*namespace*).
Fajl `mysite/urls.py`:
```python
from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = patterns('',
url(r'^polls/', include('polls.urls', namespace="polls")),
url(r'^admin/', include(admin.site.urls)),
)
```
Fajl `polls/templates/polls/index.html`:
```django
{{ question.question_text }}
```
]
---
name: forms
class: center, middle, inverse
layout: false
# Forme
---
layout: true
.section[[Forme](#sadrzaj)]
---
## Pisanje jednostavne forme
Šablon `polls/detail.html`:
```django
{{ question.question_text }}
{% if error_message %}
{{ error_message }}
{% endif %}
```
---
## Pogled
.medium[
Fajl `polls/urls.py`:
```python
url(r'^(?P\d+)/vote/$', views.vote, name='vote'),
```
Fajl `polls/views.py`:
```python
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from polls.models import Choice, Question
# ...
def vote(request, question_id):
p = get_object_or_404(Question, pk=question_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {
'question': p,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
```
]
---
## Rezultati glasanja
.medium[
Po uspešnom glasanju (POST forme) vrši se redirekcija na `polls:results` pogled.
Fajl `polls/views.py`:
```python
from django.shortcuts import get_object_or_404, render
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
```
Šablon `polls/templates/polls/results.html`:
```django
{{ question.question_text }}
{% for choice in question.choice_set.all %}
- {{ choice.choice_text }} -- {{ choice.votes }}
vote{{ choice.votes|pluralize }}
{% endfor %}
Vote again?
```
]
---
name: genericviews
class: center, middle, inverse
layout: false
# Generički pogledi
---
layout: true
.section[[Generički](#sadrzaj)]
---
## Generički pogledi
- Prethodno prikazani pogledi su često korišćeni u web aplikacijama
- Učitavanje podataka iz baze na osnovu parametra prosleđenog preko URL-a.
- Renderovanje šablona i vraćanje rezultata.
- Generički pogledi upravo predstavljaju ovaj obrazac koda.
- Dva generička pogleda: `ListView` i `DetailsView`.
---
## Prerada polls urls.py
Fajl `polls/urls.py`:
```python
from django.conf.urls import patterns, url
from polls import views
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P\d+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P\d+)/results/$', views.ResultsView.as_view(),
name='results'),
url(r'^(?P\d+)/vote/$', views.vote, name='vote'),
)
```
---
## Prerada pogleda
Fajl `polls/views.py`:
```python
...
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
...
```
---
## `ListView`
- Prikaz liste objekata.
- Podrazumevani šablon je oblika `/_list.html`
- To se može promeniti navođenjem atributa template_name
- U kontekst šablona listi objekata se pristupa preko reference
`_list` pri čemu je ime modela u *lowercase-u* (npr.
`question_list`)
- Ovo se menja preko `context_object_name`.
- Podrazumevano je listanje svih objekata modela. Ovo se može promenitit
navođenjem metode `get_queryset(self)`.
---
## `DetailsView`
- Prikaz detalja pojedinačnog objekta.
- `id` objekta se iz URL-a prosleđuje pod imenom `pk`.
- Važe ista pravila za određivanje šablona pri čemu je ime oblika
`/_detail.html`
- U kontekstu šablona instanci se pristupa po imenu modela u *lowercase-u*.
---
name: templates
class: center, middle, inverse
layout: false
# Šabloni detaljnije
---
layout: true
.section[[Šabloni](#sadrzaj)]
---
## Šabloni
- Tekstualni fajlovi koji imaju fiksne i varijablne delove.
- Koriste se za generisanje proizvoljnog tekstualnog sadržaja: HTML, JSON, XML,
CSS, JavaScrip, Java, Python, Email, izveštaji...
---
## Primer
```django
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
{{ section.title }}
{% for story in story_list %}
{{ story.tease|truncatewords:"100" }}
{% endfor %}
{% endblock %}
```
---
## Varijable konteksta
- Navode se u obliku `{{ varijabla }}`.
- Može se koristiti dot notacija za pristup atributima varijable pri čemu
je semantika sledeća:
- Prvo se pokušava po ključu rečnika
- Zatim pristup atributu ili metodi
- Na kraju se pokušava pristup po numeričkom indeksu (deo iza tačke mora biti
numerički)
- Ukoliko varijabla ne postoji u kontekstu referenca će biti renerovana na
osnovu podešavanja `TEMPLATE_STRING_IF_INVALID` što je
podrazumevano prazan string.
---
## Filteri
Na prikaz varijable se može uticati filterima.
```django
{{ value|default:"nothing" }}
Za value == None -- nothing
{{ value|length }}
Za value == [1, 2, 3] -- 3
{{ value|filesizeformat }}
Za value == 123456789 -- 117.7 MB
```
Filteri se mogu povezivati:
```django
{{ text|escape|linebreaks }}
```
Mogu imati parametre:
```django
{{ bio|truncatewords:30 }}
{{ list|join:", " }}
```
---
## Tagovi
Složenije konstrukcije oblika:
```django
{% tag %} ... sadržaj... {% endtag %}
```
Služe za implementaciju kontrole toke (petlji, uslova), učitavanje eksternih
informacija i sl.
---
## `For`
```django
{% for athlete in athlete_list %}
- {{ athlete.name }}
{% endfor %}
```
---
## `If`, `elif` i `else`
```django
{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
Athletes should be out of the locker room soon!
{% else %}
No athletes.
{% endif %}
```
```django
{% if athlete_list|length > 1 %}
Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
Athlete: {{ athlete_list.0.name }}
{% endif %}
```
---
## Nasleđivanje šablona
- Najkompleksniji i najmoćniji mehanizam Django obrađivača šablona.
- Omogućava definisanje šablona najvišeg nivoa i zatim redefiniciju i
specijalizaciju za konkretne slučajeve.
- Ovim se većina konkretnih šablona minimizuje.
---
## Nasleđivanje šablona (2)
```django
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<title>{% block title %}My amazing site{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
```
---
## Nasleđivanje šablona (3)
```django
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
{% for entry in blog_entries %}
{{ entry.title }}
{{ entry.body }}
{% endfor %}
{% endblock %}
```
---
## Nasleđivanje šablona (4)
- Roditeljski `block` tagovi treba da imaju podrazumevani sadržaj.
- Ukoliko primetite da duplirate kod u šablonima to je znak da treba da kreirate
blok i da ga smestite u roditeljski šablon i onda samo redefinišete gde je
potrebno.
- Sadržaj roditeljskog bloka se može referencirati iz bloka putem `{{
block.super }}`.
- `endblock` opciono može definisati ime što je zgodno kod većih šablona.
```django
{% block content %}
...
{% endblock content %}
```
---
## Automatski HTML escaping
- Sprečavanje *Cross Site Scripting (XSS)*.
- Django automatski uključuje *HTML escaping* za sve stringove koje renderuje.
- To je moguće isključiti za pojedine delove šablona ili na nivou celog obrađivača.
- Sledeći karakteri se konvertuju:
- `<` se konvertuje u `<`
- `>` se konvertuje u `>`
- `'` (jednostruki navodnici) se konvertuju u `'`
- `"` (dvostruki navodnici) se konvertuju u `& quot;`
- `&` se konvertuje u `&`
---
## Automatski HTML *escaping* - varijable
```django
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
```
Za `data` vrednost '<b>' rezultuje sledećim kodom
```django
This will be escaped: <b>
This will not be escaped: <b>
```
---
## Automatski HTML *escaping* - blokovi
```django
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
```
```django
Auto-escaping is on by default. Hello {{ name }}
{% autoescape off %}
This will not be auto-escaped: {{ data }}.
Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}
```
---
## Pozivi metoda u šablonima
- Moguće je pozivati metode koje nemaju parametre.
- Sintaksa je ista kao za pristup atributima.
```django
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
```
```django
{{ task.comment_set.all.count }}
```
Moguće je pozivati i korisničke metode.
```python
class Task(models.Model):
def foo(self):
return "bar"
```
```django
{{ task.foo }}
```
---
## Biblioteke tagova i filtera
- Tagovi i filteri se mogu definisati od strane korisnika ili autora aplikacija.
- Učitavaju se sa tagom `load`.
```django
{% load humanize %}
{{ 45000|intcomma }}
```
- U ovom slučaju aplikacija `django.contrib.humanize` mora biti omogućena u
konfiguraciji `INSTALLED_APPS`.
- Moguće je istovremeno učitati više biblioteka.
```django
{% load humanize i18n %}
```
---
name: testiranje
class: center, middle, inverse
layout: false
# [Testiranje](https://docs.djangoproject.com/en/1.10/topics/testing/)
---
name: static-files
class: center, middle, inverse
layout: false
# [*Static files*](https://docs.djangoproject.com/en/1.10/howto/static-files/deployment/)
---
name: styling
class: center, middle, inverse
layout: false
# [Stilizovanje aplikacija](https://docs.djangoproject.com/en/1.10/intro/tutorial06/)
---
## Reference
- [Django dokumentacija](https://docs.djangoproject.com/en/1.10/)
--
class: center, middle, theend, hide-text
layout: false
background-image: url(../theend.gif)