スポンサーリンク

DjangoGirlsTutorialをやってみる(11)記事編集ページ

前回は、「個別記事ページの作成」(Railsでいう 「show」)について扱った。

http://twosquirrel.mints.ne.jp/?p=9757

今回は、以下のところから再開する。いよいよラスト。

Django フォーム
https://djangogirlsjapan.gitbooks.io/workshop_tutorialjp/content/django_forms/

(環境)
Windows8.1
Anaconda4.1.1 (python 3.5.2)
Django1.9
(下準備)
cmd.exeを「管理者で実行」
cd c:/py/djangogirls/myproject
activate root

(1)blog/ フォルダに、forms.py を作成し、以下を記載して保存。

from django import forms

from .models import Post

class PostForm(forms.ModelForm):

    class Meta:
        model = Post
        fields = ('title', 'text',)

 

[sourcecode language=”python” padlinenumbers=”true”]
from django import forms

from .models import Post

class PostForm(forms.ModelForm):

class Meta:
model = Post
fields = (‘title’, ‘text’,)
[/sourcecode]

image

image

(2)フォームを含むページへのリンク

blog/templates/blog/base.html を開き、 div class=”page-header” タグ内にリンクを追加

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

 

[sourcecode language=”python”]
&lt;a href=&quot;{% url ‘blog.views.post_new’ %}&quot; class=&quot;top-menu&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-plus&quot;&gt;&lt;/span&gt;&lt;/a&gt;
[/sourcecode]

(変更前)
image

(変更後)
image

cmd.exe で、python manage.py runserver
ブラウザで、http://127.0.0.1:8000

image

(3)URLを追加(Railsでのルーティング、config/routes.rbと同じと思われる)。blog/urls.py の、urlpatterns = [ ] に、次の行を追加。

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

 

[sourcecode language=”python”]
url(r’^post/new/$’, views.post_new, name=’post_new’),
[/sourcecode]

(変更前)
image

(変更後)
image

image

image

AttributeError: module ‘blog.views’ has no attribute ‘post_new’
(Railsでいう、blogs_controller.rb に、post_new アクションが定義されていませんよという意味だと思われる。)

(4)blog/views.py の最初と最後に、以下を追加して保存。

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

 

[sourcecode language=”python”]
def post_new(request):
form = PostForm()
return render(request, ‘blog/post_edit.html’, {‘form’: form})
[/sourcecode]

(変更前)
image

(変更後)
image

ブラウザで、http://127.0.0.1:8000

image

(4)blog/templates/blog フォルダに post_edit.html を作成

{% csrf_token %} を、<form method> の後にちゃんと記載するのがポイントらしい。

{% 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 %}

 

[sourcecode language=”python”]
{% extends ‘blog/base.html’ %}

{% block content %}
&lt;h1&gt;New post&lt;/h1&gt;
&lt;form method=&quot;POST&quot; class=&quot;post-form&quot;&gt;{% csrf_token %}
{{ form.as_p }}
&lt;button type=&quot;submit&quot; class=&quot;save btn btn-default&quot;&gt;Save&lt;/button&gt;
&lt;/form&gt;
{% endblock %}
[/sourcecode]

image

image

ブラウザで、http://127.0.0.1:8000/post/new/

image

この時点では、TitleとTextに何か入力して、Saveしても、何もおこらない。

(5)formをsaveする

Raisだと、blogs_controller.rbで、def new (GET) と、def create (POST)の2つのアクションで、データの新規作成を行っていたが、Djangoでは、blog/views.pyで、def post_new(request): の中で、

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

と記載するらしい。

blog/views.py の、最初の方に、以下を追加。

from django.shortcuts import redirect

さらに、def post_new(request): 以下を、このように変更。

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.save()
            return redirect('blog.views.post_detail', pk=post.pk)
    else:
        form = PostForm()
    return render(request, 'blog/post_edit.html', {'form': form})

 

[sourcecode language=”python”]
def post_new(request):
if request.method == &quot;POST&quot;:
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect(‘blog.views.post_detail’, pk=post.pk)
else:
form = PostForm()
return render(request, ‘blog/post_edit.html’, {‘form’: form})
[/sourcecode]

(変更前)
image

(変更後)
image

ブラウザで、http://127.0.0.1:8000/post/new/

image

image

image

新規Postは投稿されるが、公開設定にしないと、公開はされない。
(Admin以外で、公開設定する手段が今のところない)

何も入力しないで Save だけクリックすると、バリデーションエラーが表示される。

image

ちなみに、https://djangogirlsjapan.gitbooks.io/workshop_tutorialjp/content/django_forms/によると、「ここでは現在、Djangoの管理画面と同様に、ログイン状態で操作しています。」だそうだ。むむ。。。

なお、Publish ボタンの設置や、Deleteについては、以下に英語で記載されている。やあはり、CRUD(Create, Read, Update, Delete)はできるようになっておきたいところ。いずれ、、、

https://github.com/DjangoGirls/tutorial-extensions/blob/master/homework/README.md

(6)フォームの編集(Edit)

● blog/templates/blog/post_detail.html の post.published_date の記載部分の後に、以下を記載して保存。

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

 

[sourcecode language=”python”]
&lt;a class=&quot;btn btn-default&quot; href=&quot;{% url ‘post_edit’ pk=post.pk %}&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-pencil&quot;&gt;&lt;/span&gt;&lt;/a&gt;
[/sourcecode]

どこかで間違えたのが覚えていないが、最終的には、以下のようにするらしい。

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

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

 

[sourcecode language=”python”]
{% extends ‘blog/base.html’ %}

{% block content %}
&lt;div class=&quot;date&quot;&gt;
{% if post.published_date %}
{{ post.published_date }}
{% endif %}
&lt;a class=&quot;btn btn-default&quot; href=&quot;{% url ‘post_edit’ pk=post.pk %}&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-pencil&quot;&gt;&lt;/span&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;h1&gt;{{ post.title }}&lt;/h1&gt;
&lt;p&gt;{{ post.text|linebreaks }}&lt;/p&gt;
{% endblock %}
[/sourcecode]

(変更前)
image

(変更後)
image

● blog/urls.py の、urlpatterns = [ ] に、次の行を追加。

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

 

[sourcecode language=”python”]
url(r’^post/(?P&lt;pk&gt;[0-9]+)/edit/$’, views.post_edit, name=’post_edit’),
[/sourcecode]

image

● blog/views.py の最後に、以下を追加。

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.save()
            return redirect('blog.views.post_detail', pk=post.pk)
    else:
        form = PostForm(instance=post)
    return render(request, 'blog/post_edit.html', {'form': form})

 

[sourcecode language=”python”]
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == &quot;POST&quot;:
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect(‘blog.views.post_detail’, pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, ‘blog/post_edit.html’, {‘form’: form})
[/sourcecode]

post_view とほとんど同じに見えるが、

<1>URLから pk パラメータを渡し、
<2>Post モデルを get_object_or_404(Post, pk=pk) で取得し、
<3>フォームを保存する際、この記事をインスタンスとしてフォームを作成。

ところが違うらしい。

image

ブラウザで、http://127.0.0.1:8000/post/1/

image

image

image

たしかに編集はできるようになった。

Rails から入った私には、なんだかここら辺はややこしく感じるが、どうなんだろうか。。。RailsでいるStrongParametersに相当するものはどうなっているのかなあって思ったりするが、私にはわからない。

(参考)フレームワークに見る Web セキュリティ対策
Jxck_が2015/07/24に投稿(2015/08/17に編集)
http://qiita.com/Jxck_/items/ec8e928f69d099b25764

いわゆるMass Assignment対策は、Formsを利用することでなんとかなっているらしい。。。(なるほどわからん)

Mass Assignment – Security Part 10
09 AUGUST 2012 on django, django-nyc-security, forms, models, mozilla, security
https://coffeeonthekeyboard.com/mass-assignment-security-part-10-855/

(7)Herokuにデプロイ

git add -A .
git commit -m “Added views to create/edit blog post inside the site.”
git push heroku master

image
image

Herokuの自分のアドレスを確認。
私の場合は、今回は、
http://floating-beach-54856.herokuapp.com/

image

image

image

image

うまくいっているようである。

Bitbucketに登録。

(既に、git addと、git commitはしてあるので、)Sourcetreeのプッシュボタンをクリック。

image

プッシュ先を、(私の場合は)myproject にして、プッシュ。

image

Bitbucketで確認。

image

これでいったん終了。しかし、deleteと、publishボタンの追加は、いずれしなければ、、、

https://github.com/DjangoGirls/tutorial-extensions/blob/master/homework/README.md

スポンサーリンク