Django

Szablony i Formularze

16 lutego 2016 21:00
Marek Leszczyński

Ten artykuł jest kontynuacją czteroczęściowego bloku o Django. Jeśli jeszcze tego nie zrobiłeś powinieneś najpierw odwiedzić: Wprowadzenie do Django, Django i bazy danych oraz Django i widoki

Co to jest ten szablon ?

Django jako web framework potrzebuje w jakiś sposób generować HTML dynamicznie. Z pomocą przychodzą nam szablony. Szablony są to krótko mówiąc pliki html zawierające statyczne części naszego HTML (np. menu, logo itp.) oraz specjalny kod który określa jak wyświetlać dynamiczne dane. Django domyślnie w szablonach używa Django template language (DTL). Oczywiście możemy użyć innego silnika do generowania szablonów np. Jinja2 lecz w naszym przykładzie wykorzystamy wbudowany DTL.

Wiemy co to szablon, ale jak go użyć ?

Zaczynamy od ustawień naszego projektu i wskazujemy jaki silnik chcemy używać. Django 1.9 domyślne ma już skonfigurowany silnik w ustawieniach lecz w wypadku gdy takiej konfiguracji nam brakuje, bądź chcemy zmienić silnik należy zmienić to w ustawieniach. Aby to zrobić otwieramy plik settings.py i następnie dodajemy:

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ],
    },
},

]

Dzięki opcji APP_DIRS': True, Django będzie szukał szablonów również w katalogach naszych aplikacji. Wszystkie szablony jakie chcemy używać w konkretnej aplikacji umieszczamy w katalogu templates (<app_name>/templates/<template_name>).

Utwórzmy sobie teraz nowy szablon o nazwie hello_world.html w katalogu blog/templates

<html>
    <head>
      <title></title>
    </head>
    <body>
      <p>Hello world!</p>
    </body>
</html>

Mamy utworzony szablon więc czas na użycie go w widoku. W poprzednim artykule przy widokach dziedziczyliśmy z klasy View. Wykorzystując szablony będziemy dziedziczyć z klasy TemplateView.

# views.py
from django.views.generic import TemplateView

# Create your views here.


class HelloWorld(TemplateView):
    template_name = 'hello_world.html'


# urls.py
from django.conf.urls import url
from django.contrib import admin

from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^hello_world/', views.HelloWorld.as_view()), ]

Uruchamiamy nasz serwer komendą runserver i przechodzimy na nasz adres http:127.0.0.1:8000/hello_world/

No dobra, a co z dynamicznymi danymi ?

Wszystkie dane jakie chcemy zawrzeć w naszym szablonie przekujemy jako context. Context jest to słownik z naszymi danymi, gdzie jako klucz podajemy nazwę pod jaką ma być to widoczne w szablonie a jako wartość umieszczamy interesujące nas dane (mogą to być słowniki, listy, statyczne wartości itp.). Utwórzmy więc nowy szablon pod nazwą hello_name.html

<html>
    <head>
      <title></title>
    </head>
    <body>
      <p>Hello {{name}}!</p>
    </body>
</html>

A następnie dodajmy nowy widok.

# views.py
class HelloName(TemplateView):
    template_name = 'hello_name.html'

    def get_context_data(self, name, **kwargs):
        context = {
            'name': name
        }

        return context


# urls.py
urlpatterns = [
    ...
    url(r'^hello_name/(?P<name>\w+)/', views.HelloName.as_view()),
]

Wyświetlmy teraz listę wszystkich członków rodziny z naszej bazy. Wykorzystamy do tego szablon, który utworzyliśmy w poprzednim artykule:

<html>
  <head>
    <title></title>
  </head>
  <body>
    <ul>
      {% for member in members %}
        <li>{{ member.imie }} {{ member.nazwisko }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

I użyjemy go w naszym widoku:

# views.py
class CzlonkowieRodziny(TemplateView):
    template_name = 'lista_czlonkow.html'

    def get_context_data(self, **kwargs):
        context = {
            'members': CzlonekRodziny.objects.all()
        }

        return context


# urls.py
urlpatterns = [
    ...
    url(r'^czlonkowie_rodziny/', views.CzlonkowieRodziny.as_view()),
]

Forumalrze

W naszych aplikacjach często wykorzystujemy formularze w celu dodania nowych wpisów lub wyszukiwania tych, które nas interesują. Często obsługa formularzy staje się problematyczna. Django dostarcza nam możliwość dynamicznego generowania formularzy i używania ich w szablonach. Dzięki temu nie musimy się martwić o sprawdzanie poprawności wprowadzonych danych czy o budowanie formularzy ponieważ Django robi to generalnie za nas. Generalnie przy tworzeniu formularzy będą nas interesowały pliki forms.py, views.py, urls.py oraz pliki z szablonami.

forms.py

from django import forms

class HelloForm(forms.Form):
    name = forms.CharField(label=u'Your name', max_length=20)

views.py

from django.views.generic import TemplateView
from django.http import HttpResponseRedirect

from blog.forms import HelloForm

class SayHello(TemplateView):
    template_name = 'say_hello.html'

    def get_context_data(self, **kwargs):
        context = {
            'form': HelloForm()
        }

        return context

    def post(self, request):
        form = HelloForm(request.POST)

        if form.is_valid():
            return HttpResponseRedirect(
                '/hello_name/{}'.format(form.cleaned_data['name'])
            )
        return HttpResponseRedirect('/say_hello/')

urls.py

urlpatterns = [
    ...
    url(r'^say_hello/', views.SayHello.as_view()),
]

say_hello.html

<html>
    <head>
      <title></title>
    </head>
    <body>
      <form action="/say_hello/" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="Submit" />
      </form>
    </body>
</html>

Uruchamiamy nasz serwer komendą runserver i przechodzimy na nasz adres http:127.0.0.1:8000/say_hello/

Formularze i modele

Django umożliwia nam generowanie formularzy na podstawie naszych modeli. Jest to bardzo przydatne gdy operujemy na naszych modelach wyszukując, dodając, modyfikując oraz usuwając je. Nie musimy martwić się o to, czy podaliśmy poprawne dane, czy pola w formularzach zostały dodane poprawnie itp.

forms.py

from blog.models import CzlonekRodziny

class CzlonekRodzinyForm(forms.ModelForm):
    class Meta:
        model = CzlonekRodziny
        fields = ['imie', 'nazwisko', 'wiek']

views.py

class WyszukajDodajCzlonka(TemplateView):
    template_name = 'lista_czlonkow.html'

    def get_queryset(self, request):
        qs = CzlonekRodziny.objects.filter()
        if 'imie' in request.GET and request.GET['imie']:
            qs = qs.filter(imie=request.GET['imie'])
        if 'nazwisko' in request.GET and request.GET['nazwisko']:
            qs = qs.filter(nazwisko=request.GET['nazwisko'])
        if 'wiek' in request.GET and request.GET['wiek']:
            qs = qs.filter(wiek=request.GET['wiek'])

        return qs

    def get_context_data(self, data=None, **kwargs):
        context = {
            'form': CzlonekRodzinyForm(data),
            'members': self.get_queryset(self.request)
        }

        return context

    def post(self, request):
        form = CzlonekRodzinyForm(request.POST)

        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/czlonkowie_rodziny/')
        else:
            return render(
                request, self.template_name,
                self.get_context_data(request.POST)
            )

        return HttpResponseRedirect('/czlonkowie_rodziny/')

urls.py

urlpatterns = [
    ...
    url(r'^czlonkowie_rodziny/', views.WyszukajDodajCzlonka.as_view()),
]

lista_czlonkow.html

<html>
  <head>
    <title></title>
  </head>
  <body>
    <p>Dodaj członka</p>
    <form action="/dodaj_czlonka/" method="post">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Submit" />
    </form>
    <p>Wyszukaj członka</p>
    <form action="/dodaj_czlonka/" method="get">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Submit" />
    </form>
    <ul>
      {% for member in members %}
        <li>{{ member.imie }} {{ member.nazwisko }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

Zagnieżdżanie szablonów

Jak pewnie już zauważaliście w poprzednim przykładzie w szablonie zrobił się bałagan i stał się on nieczytelny. Aby nad tym zapanować wystarczy użyć zagnieżdżania. Szablony Djangowe dają nam możliwość wydzielenia części naszych szablonów i umieszczenia ich w osobnych plikach a następnie można jest zaimportować do naszego głównego pliku.

_wyszukaj_czlonka.html

 <p>Wyszukaj członka</p>
 <form action="/czlonkowie_rodziny/" method="get">
   {% csrf_token %}
   {{ form }}
   <input type="submit" value="Submit" />
 </form>

_dodaj_czlonka.html

<p>Dodaj członka</p>
<form action="/czlonkowie_rodziny/" method="post">
  {% csrf_token %}
  {{ form }}
  <input type="submit" value="Submit" />
</form>

_lista_czlonkow.html

<ul>
  {% for member in members %}
    <li>{{ member.imie }} {{ member.nazwisko }}</li>
  {% endfor %}
</ul>

czlonkowie_rodziny.html

<html>
  <head>
    <title></title>
  </head>
  <body>
    {% include "_wyszukaj_czlonka.html" %}
    {% include "_dodaj_czlonka.html" %}
    {% include "_lista_czlonkow.html" %}
  </body>
</html>

Mając nasz główny szablon podzielony w ten sposób jesteśmy w bardzo łatwy sposób zapanować nad naszym kodem w szablonach. Więcej na temat szablonów oraz dostępnych template tagów znajdziemy na https://docs.djangoproject.com/es/1.9/ref/templates/builtins/ .

Comments