Skip to content

Latest commit

 

History

History
executable file
·
397 lines (274 loc) · 16.9 KB

README.md

File metadata and controls

executable file
·
397 lines (274 loc) · 16.9 KB

Django formuláře

Poslední věc, kterou chceme udělat na našich webových stránkách, je vytvořit příjemný způsob, jak přidávat a upravovat příspěvky na blogu. Django admin je super, ale je to dost těžko přizpůsobitelné a upravitelné do hezčí podoby. S formuláři budeme mít absolutní moc nad naším rozhraním - můžeme dělat téměř cokoliv co si dokážeme představit!

Pěkná věc na Django formulářích je, že je můžeme definovat úplně od počátku nebo vytvořit ModelForm, který uloží výsledek z formuláře pomocí modelu.

To je přesně to, co chceme udělat: vytvoříme formulář pro náš model Post.

Stejně jako všechny důležité části Djanga, formuláře mají své vlastní soubory: forms.py.

Potřebujeme vytvořit soubor s tímto názvem v našem adresáři blog.

    blog
     └── forms.py

OK, pojďme ho otevřít a zadej následující kód:

     from django import forms

     from .models import Post

     class PostForm(forms.ModelForm):
         class Meta:
             model = Post
             fields = ('title', 'text',)

Musíme nejdříve importovat Django formuláře (z django import forms) a samozřejmě náš Post model (from .models import Post).

PostForm, jak asi předpokládáš, je jméno našeho formuláře. Musíme říct Djangu, že tento formulář je ModelForm (tak pro nás bude moci Django udělat něco magického) - zodpovědný za to bude forms.ModelForm.

Dále zde máme třídu Meta, kde řekneme Djangu, který model by měl být použít k vytvoření tohoto formuláře (model = Post).

Nakonec můžeme Djangu říct co se má stát s jednotlivými poli našeho formuláře. V tomto případě chceme pouze zobrazit pole nadpis/title a text - autor by měl být člověk, který je aktuálně přihlášen (vy!) a created_date by mělo být automaticky nastaveno, když vytvoříme příspěvek (tj. v kódu).

A je to! Vše, co musíme udělat, je použít formulář ve view a zobrazit ho v šabloně.

Takže ještě jednou budeme vytvářet: odkaz na stránky, URL, pohled a šablonu.

Odkaz na stránku s formulářem

Je tedy čas k otevření souboru blog/templates/blog/base.html. Přidáme odkaz do div s názvem page-header:

    <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>

Všimni si, že chceme zavolat náš nový view post_new.

Po přidání řádku, by měl tvůj soubor html vypadat takto:

    {% load staticfiles %}
    <html>
          <head>
             <title>Django Girls blog</title>
             <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
             <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
             <link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
             <link rel="stylesheet" href="{% static 'css/blog.css' %}">
          </head>
          <body>
             <div class="page-header">
                 <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
                 <h1><a href="/">Django Girls Blog</a></h1>
             </div>
             <div class="content container">
                 <div class="row">
                     <div class="col-md-8">
                         {% block content %}
                         {% endblock %}
                     </div>
                 </div>
             </div>
          </body>
    </html>

Po uložení a obnovení stránky http://127.0.0.1:8000 patrně uvidíš známou NoReverseMatch chybu, dobrá?

URL

Otevři blog/urls.py a přidej řádek:

    url(r'^post/new/$', views.post_new, name='post_new'),

A výsledný kód bude vypadat takto:

     from django.conf.urls import include, url
     from . import views

    urlpatterns = [
         url(r'^$', views.post_list, name='post_list'),
         url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
         url(r'^post/new/$', views.post_new, name='post_new'),
    ]

Po aktualizaci webu vidíme AttributeError, protože nemáme post_new view implementován. Přidejme ho právě teď.

post_new view

Musíme otevřít soubor blog/views.py a přidat následující řádky:

    from .forms import PostForm

a náš view:

    def post_new(request):
         form = PostForm()
         return render(request, 'blog/post_edit.html', {'form': form})

Chceš-li vytvořit nový formulář Post, musíš zavolat PostForm() a předat ho do šablony. Za chvilku se vrátíme k tomuto view, ale teď pojďme rychle vytvořit šablonu formuláře.

Šablona

Musíme vytvořit soubor post_edit.html v adresáři blog/templates/blog. Pro vytvoření formuláře potřebujeme udělat několik věcí:

  • musíme zobrazit formulář. Můžeme to udělat například jednoduchým {% raw %}{{ form.as_p }}{% endraw %}.
  • formulář musíme obalit HTML značkou (přidej řádek nad formulář): < method="POST" >... < / form >
  • potřebujeme tlačítko Uložit. Vytvoříme HTML tlačítko: < button type="submit"> Uložit < / button>
  • a nakonec těsně za otevírací značku < form... > musíme přidat {% raw %}{% csrf_token %}{% endraw %}. Toto je velmi důležité, protože tak vytvoříme bezpečnější formulář! Django si bude stěžovat v okamžiku, kdy se pokusíš uložit formulář, pokud zapomeneš na tuto část:

CSFR zakázaná stránka

OK pojďme se podívat, jak by mělo vypadat HTML v post_edit.html:

    {% extends 'blog/base.html' %}

    {% block content %}
         <h1>New post</h1>
         <form method="POST" class="post-form">{% csrf_token %}
             {{ form.as_p }}
             <button type="submit" class="save btn btn-default">Save</button>
         </form>
    {% endblock %}

Čas aktualizovat naší stránku! Hurá! Zobrazil se formulář!

Nový formulář

Ale ještě cvhilku počkej! Když něco zadáš do polí titulek a text a pokusíš se formulář uložit - co se stane?

Nic! Jsme opět na stejné stránce a náš text je pryč... a nepřidal se žádný nový příspěvek. Tak co se stalo?

Odpověď je: nic. Musíme ještě trochu zapracovat na našem view.

Ukládání formuláře

Otevři znovu blog/views.py. Vše, co máme aktuálně v post_new je toto:

    def post_new(request):
         form = PostForm()
         return render(request, 'blog/post_edit.html', {'form': form})

Když odešleme formulář, vrátíme se zpět na ten samý view, ale tentokrát obdržíme nějaká další data v request, přesněji v request.POST (pojmenování nemá nic společného s naším "post" v blogu, vychází to z toho, že "zasíláme" data). Pamatuješ si, že v našem HTML souboru ve <form> jsme definovali proměnnou method="POST"? Všechna pole z formuláře jsou nyní k dispozici v proměnné request.POST. Neměla bys přejmenovávat POST na něco jiného (jediná další platná hodnota proměnné method je GET, ale nyní nemáme čas vysvětlovat rozdíly).

Takže v našem view máme dvě samostatné situace ke zpracování. První: když načteme stránku poprvé a chceme dostat prázdný formulář. Druhou: když se vrátíme zpět do našeho view s vyplněnými daty formuláře. Takže musíme přidat podmínku (použijeme na to -if).

    if request.method == "POST":
         [...]
    else:
         form = PostForm()

Je to čas k vyplnění mezer [...]. Je-li method POST pak chceme vytvořit PostForm s údaji z formuláře, že? Udělejme to:

    form = PostForm(request.POST)

Snadné! Další věc je zjistit, zda-li je formulář vpořádku (všechna požadovaná pole jsou vyplněna a neobsahují špatné hodnoty). Uděláme to pomocí form.is_valid().

Zkontrolujeme, je-li formulář platný, a pokud ano, můžeme ho uložit!

    if form.is_valid():
         post = form.save(commit=False)
         post.author = request.user
         post.published_date = timezone.now()
         post.save()

V podstatě, tady máme dvě věci: ukládáme formulář pomocí form.save a přidáváme autora (vzhledem k tomu, že v PostForm nebylo žádné pole Autor v a toto pole je požadováno!). commit=False znamená, že ještě nechceme Post uložit pomocí modelu - chceme nejdříve přidat autora. Většinu času budeš používat form.save(), bez commit=False, ale v tomto případě to potřebujeme takto udělat. post.save() zachová změny (přidá autora) a vytvoří nový příspěvek na blogu!

A nakonec, by bylo úžasné, pokud bychom okamžitě přešli na stránku post_detail s nově vytvořeným příspěvkem, že ano? K tomu potřebujeme jeden další import:

    from django.shortcuts import redirect

Přidej to na samý začátek souboru. A nyní můžeš říci: Jdi na stránku post_detail s nově vytvořeným příspěvkem.

    return redirect('blog.views.post_detail', pk=post.pk)

blog.views.post_detail je název pohledu, na který chceme jít. Pamatuj si, že tento view vyžaduje proměnnou pk. Pro předání do view použijeme pk=post.pk, kde post je nově vytvořený příspěvek!

OK mluvili jsme hodně, ale pravděpodobně chceš vidět, jak nyní celý view vypadá nyní, nemám pravdu?

    def post_new(request):
        if request.method == "POST":
            form = PostForm(request.POST)
            if form.is_valid():
                post = form.save(commit=False)
                post.author = request.user
                post.published_date = timezone.now()
                post.save()
                return redirect('blog.views.post_detail', pk=post.pk)
        else:
            form = PostForm()
        return render(request, 'blog/post_edit.html', {'form': form})

Pojďme se podívat, jestli to funguje. Přejdi na stránku http://127.0.0.1:8000/post/new /, přidej title a text, ulož jej... a hle! Byl přidán nový příspěvek na blog a my jsme přesměrováni na stránku post_detail!

Možná jsi si všimla, že jsme nastavili proměnnou publish_date před uložením post. Později si ukážeme tlačítko Publikovat v Django girls tutorial: rozšíření.

To je úžasné!

Ověření formuláře

A teď ti ukážeme jak cool jsou formuláře v Djangu. Blog příspěvek musí mít title a text pole. V našem modelu Post jsme neřekly (na rozdíl od published_date), že jsou tato pole nepovinná, takže Django ve výchozím nastavení očekává, že budou vyplněná.

Pokus se uložit formulář bez nadpisu a textu. Hádej, co se stane!

Ověření formuláře

Django se stará o ověřování, že všechna pole v našem formuláři jsou správně. Není to úžasné?

Jelikož jsme nedávno použili Django admin rozhraní, systém si v současné době myslí, že jsme přihlášeni. Existuje několik situací, které by mohly vést k našemu odhlášení (zavření prohlížeče, restartováním DB, atd.). Pokud dostaneš chybu, při vytváření příspěvku, upozorňující na nepřihlášeného uživatele, zamiř na http://127.0.0.1:8000/admin stránky a přihlas se znovu. Tím se dočasně vyřeší tento problém. Existuje trvalá oprava, která na tebe čeká v domácím úkolu: zabezpeč svůj web! kapitola za hlavním tutorialem.

Chyba přihlášení

Upravit formulář

Teď víme, jak přidat nový formulář. Ale co když chceme upravit stávající? Je to velmi podobné, tomu jsme právě dělali. Vytvoříme teď některé důležité věci rychle (pokud něčemu neporozumíš, měla bys požádat svého kouče, nebo se podívat do předchozích kapitol, protože jsme již všechny tyto kroky probírali).

Otevři blog/templates/blog/post_detail.html a přidej následující řádek:

    <a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>

tak, aby šablona vypadala takto:

    {% extends 'blog/base.html' %}
    {% block content %}
         <div class="post">
             {% if post.published_date %}
                 <div class="date">
                     {{ post.published_date }}
                 </div>
             {% endif %}
             <a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
             <h1>{{ post.title }}</h1>
             <p>{{ post.text|linebreaks }}</p>
         </div>
    {% endblock %}

V blog/urls.py přidej tento řádek:

         url(r'^post/(?P<pk>[0-9]+)/edit/$', views.post_edit, name='post_edit'),

Znovu použijeme šablonu blog/templates/blog/post_edit.html, takže jediné, co chybí je view.

Otevři blog/views.py a přidej na samý konec souboru:

    def post_edit(request, pk):
         post = get_object_or_404(Post, pk=pk)
         if request.method == "POST":
             form = PostForm(request.POST, instance=post)
             if form.is_valid():
                 post = form.save(commit=False)
                 post.author = request.user
                 post.published_date = timezone.now()
                 post.save()
                 return redirect('blog.views.post_d etail', pk=post.pk)
         else:
             form = PostForm(instance=post)
         return render(request, 'blog/post_edit.html', {'form': form})

Vypadá to téměř úplně stejně jako náš view post_new? Ale ne zcela. První věc: máme navíc parametr pk z adresy URL. Další: získáme model Post, který chceme upravit, pomocí get_object_or_404 (Post, pk = pk). Když potom vytvoříme formulář, předáme tento post jako parametr instance, když ukládáme formulář:

    form = PostForm(request.POST, instance=post)

a když jsme právě otevřely formulář s tímto příspěvkem pro úpravu:

    form = PostForm(instance=post)

Ok, vyzkoušejme jestli to funguje! Pojďme na stránku post_detail. Tam by mělo být tlačítko Edit v pravém horním rohu:

Tlačítko Upravit

Po klepnutí na něj se zobrazí formulář s naším příspěvkem:

Upravit formulář

Neboj se změnit název nebo text a ulož změny!

Blahopřejeme! Tvoje aplikace je čím dál více kompletní!

Pokud potřebuješ více informací o Django formulářích měla bys přečíst dokumentaci: https://docs.djangoproject.com/en/1.8/topics/forms/

Zabezpečení

Být schopna vytvářet nové příspěvky pouhým klepnutím na odkaz je super! Právě teď ale každý, kdo přijde na tvé stránky, je schopen odeslat nový příspěvek na blog a to asi nechceš. Udělejme to tak, že se tlačítko zobrazí tobě, ale nezobrazí se nikomu jinému.

V blog/templates/blog/base.html najdeš naši page-header div a kotevní tag, který jsme tam dali dříve. Řádek by měl vypadat nějak takto:

    <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>

Chceme přidat další {% if %} příkaz, který zajistí, že se odkaz zobrazí pouze pro uživatele, kteří jsou přihlášeni jako admin. Aktuálně, jsi jím pouze ty! Změň tag < a >, aby vypadal takto:

    {% if user.is_authenticated %}
         <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
    {% endif %}

Tento {% if %} způsobí, že odkaz bude odeslán do prohlížeče pouze pokud je uživatel, který požaduje stránku, přihlášený. To zcela nezabrání možnosti vytvářet nové záznamy, ale je to dobrý první krok. Lepší zabezpečení vysvětlíme v rozšířené lekci.

Vzhledem k tomu, že jsi pravděpodobně přihlášená, pokud aktualizuješ stránku, neuvidíš žádnou změnu. Načtení stránky v novém prohlížeči nebo anonymní okně, se odkaz již neukáže!

Ještě jedna věc: čas nasadit aplikaci!

Uvidíme, jestli to funguje na PythonAnywhere. Čas na další nasazení!

  • Za prvé commitni nový kód a pošli ho na Github
    $ git status
    $ git add --all .
    $ git status
    $ git commit -m "Added views to create/edit blog post inside the site."
    $ git push
    $ cd my-first-blog
    $ source myvenv/bin/activate
    (myvenv)$ git pull
    [...]
    (myvenv)$ python manage.py collectstatic
    [...]
  • Nakonec, skoč na kartu Web a klikni na Reload.

A mělo by to být! Gratulujeme :)