超初心者用: Djangoでウェブアプリを作る ⑥ユーザーによるentryの編集

前回に引き続き、今回もユーザーによるデータの変更に対応していきます。今回はユーザー自身が entry を編集できるようにします。

Entry の編集

edit_entry URL

entry を編集するURLには id が必要になります。practicing_logs/urls.py はこんな感じになります。

# practicing_logs/urls.py

#--省略--
urlpatterns = [
    #--省略--
    #  entry を編集するためのページ
    url(r'^edit_entry/(?P<entry_id>\d+)/$', views.edit_entry, name='edit_entry'),
]

もうこりごりするほどこの形式を見てきましたね。 http://localhost:8000/edit_entry/1/ のリンクで entry_id が1の entry を編集できるページに飛びます。URLがそれとマッチするときedit_entry() の view function が呼ばれます。

edit_entry() view function

GET リクエストを edit_entry ページが受け取ったとき、 edit_entry() が entry を変更するためのフォームを返します。POST リクエストを受け取ったときは、修正された文章をデータベースに保存します。

#views.py

from django.shortcuts import render
#--省略--
from .models import Topic, Entry   #<----ここ
from .forms import TopicForm, EntryForm
#--省略--


def edit_entry(request, entry_id):
    """すでにある entry の編集"""
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic
    
    if request.method != 'POST':
        # 何も変更がない時
        form = EntryForm(instance=entry)
    else:
        # POST データが送信されたとき
        form = EntryForm(instance=entry, data=request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('practicing_logs:topic', args=[topic.id]))

    context = {'entry': entry, 'topic': topic, 'form': form}
    return render(request, 'practicing_logs/edit_entry.html', context)

まずは Entry モデルをインポートしなければなりません。

entry = Entry.objects.get(id=entry_id)

ユーザーが編集する entry を entry_id を使って入手します。

if request.method != 'POST':

もしリクエストがPOST以外であるならば、現在の entry の内容を持った EntryForm を作成します。つまり同じ内容を上書きする感じです。

form = EntryForm(instance=entry, data=request.POST)

リクエストがPOSTだった場合は、instance=entry と data=request.POST を送ります。これはPOSTのリクエストからのユーザーが入力した文章で form instance を作ります。そのあと整合性をチェックし、合格すれば save() を行ってデータベースに保存します。最後にはトピックページにリダイレクトします。

edit_entry template

# edit_entry

{% extends "practicing_logs/base.html" %}
{% block content %}
<p><a href="{% url 'practicing_logs:topic' topic.id %}">{{ topic }}</a></p>

<p>Edit entry:</p>

<form action="{% url 'practicing_logs:edit_entry' entry.id %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">save changes</button>
</form>
{% endblock content %}

action argument はedit_entry() にフォームを送ります。そして view function が entry を編集します。

edit_entry ページをリンクする

edit_entry ページをそれぞれの entry から飛べるようにします。

#topic.html

#--省略--
{% for entry in entries %}
    <li>
        <p>{{ entry.date_added | date: 'M d, Y H:i' }}</p>
        <p>{{ entry.text | linebreakes }}</p>
        <p>
            <a href="{% url 'practicing_logs:edit_entry' entry.id %}">edit entry</a>
        </p>
    </li>
#--省略--

それぞれの entry の本文の下に edit ページへのリンクを設置しました。entry id でどの entry を編集するかを識別しています。見た目はこんな感じです。

f:id:kendamaaa:20200428153830j:plain

次回

次はユーザーそれぞれにアカウントを与えて、トピックと entry を管理していきます。

超初心者用: Djangoでウェブアプリを作る ⑤ユーザーによるentryの追加

前回までにユーザーが新しいトピックを追加することができるようになりました。今回は Entry を入力できるようにします。手順はおなじみの、URL の決定、view function と template の記入、ページへのリンクです。しかし、まずは form.py にクラスを追加します。

Entry ModelForm

Entry モデルのためのフォームを作らなければなりません。今回は少し異なります。

# form.py

from django import forms
from .models import Topic, Entry

class TopicForm(forms.ModelForm):
 # --省略--

class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['text']
        labels = {'text': ''}
        widgets = {'text': forms.Textarea(attrs={'cols': 80})}

まずは、Topic と同様に Entry クラスをインポートします。Metaクラスをネストしてある forms.ModelForm からの EntryForm クラスを作ります。ここでも text フィールドを与えます。

widgets = {'text': forms.Textarea(attrs={'cols': 80})}

widgets attribute を使って、text box の横幅の大きさを設定します。今回は横に80 columns に設定しています。ちなみにデフォルトでは40となっています。

new_entry URL

新しい entry を追加するときには、特定のトピックに結び付けるのでURL に topic_id を含めなければなりません。

# practicing_logs/urls.py

#--省略--
urlpatterns = [
    #--省略--
    # 新しい entry の追加ページ
    url(r'^new_entry/(?P<topic_id>\d+)/$', views.new_entry, name='new_entry'),
]

new_entry() view function

この view function は new_topic とほとんど一緒です。

# views.py

from django.shortcuts import render
#--省略--
from .models import Topic
from .forms import TopicForm, EntryForm
#--省略--

def new_entry(request, topic_id):
    """特定のトピックに entry を追加"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 空欄
        form = EntryForm()
    else:
        # POST data が送られた
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            new_entry.save()
            return HttpResponseRedirect(reverse('practicing_logs:topic', args=[topic_id]))

    context = {'topic': topic, 'form': form}
    return render(request, 'practicing_logs/new_entry.html', context)

先ほど作成した EntryForm をインポートします。new_entry() はURL から受け取る topic_id parameter を持っています。

topic = Topic.objects.get(id=topic_id)

受け取った id に呼応するトピックを取得します。

if request.method != 'POST':

リクエストが POST か GET なのかを確認します。GET であれば空欄の EntryForm を作成します。POST であれば、入力されたデータで処理します。

if form.is_valid():

入力されたデータが適切なフォーマットであるかを確認し、適切であれば save() を実行します。commit==False はデータベースに保存することなく、entry を作成し、new_entry に保存します。new_entry のトピックを決定したら save() を実行します。これはデータベースに保存されます。

return HttpResponseRedirect(reverse('practicing_logs:topic', args=[topic_id]))

無事に追加できたら、topic ページにリダイレクトします。

new_entry template

このテンプレートも new_topic の時のものと似ています。

# new_entry.html

{% extends "practicing_logs/base.html" %}
{% block content %}
<p><a href="{% url 'practicing_logs:topic' topic.id %}">{{ topic }}</a></p>

<p>Add a new entry:</p>
<form action="{% url 'practicing_logs:new_entry' topic.id %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name='submit'>add entry</button>
</form>

{% endblock content %}

<p><a href="{% url 'practicing_logs:topic' topic.id %}">{{ topic }}</a></p>

一番上にはどのトピックに入力しようとしているのかを分かりやすくするために、トピック名を表示させます。

<form action="{% url 'practicing_logs:new_entry' topic.id %}" method='post'>

action argument は URL の topic_id を含んでいます。

new_entry ページにリンクする

それぞれのトピックページから entry を追加するページに飛べるようにします。

# topic.html

{% extends "practicing_logs/base.html" %}
{% block content %}
<p>Topic: {{ topic }}</p>

<p>Entries:</p>

<p>
<a href="{% url 'practicing_logs:new_entry' topic.id %}">add new entry</a>
</p>

<ul>
 #--省略--
 </ul>
{% endblock content %}

既存の entry を見せる前に 新しい entry を追加するためのリンクを継和得ます。下の画像のような感じになります。

f:id:kendamaaa:20200428063147j:plain

超初心者用: Djangoでウェブアプリを作る ④ユーザによるTopicの追加

ウェブアプリで重要な要素といえば、ユーザーがそれぞれのアカウントを持つことできるようにすることです。まずは、ユーザーが独自のトピックと entry を追加できるようなフォームを作ります。さらにDjango がどのようにハッカーからの攻撃に対処するのかを簡単に説明していきます。

ユーザーのデータを入力させる

ユーザーのアカウントを作るシステムを作る前に、データを入力させるページを作ります。新しいトピックや entry を加えたり、編集したり。 現在は superuser だけが admin site を通してのみこれらを行うことができます。一般のユーザーには admin site に入ってきてほしくないので、Djangoのデータ入力フォームを使用します。

トピックモデルフォーム

ユーザーに情報を入力させるページはフォームと呼ばれます。情報が入力されたとき、必ず情報の形式が正しいかどうかを確認しなければなりません。確認できたら、その情報をデータベースに保存します。ありがたいことに、Django はこれらをほとんど自動的に行ってくれます。

フォームを作る最もシンプルな方法は ModelForm を使用することです。まずは、forms.py を作ります。models.py と同じ階にこのファイルを作ってください。

# forms.py

from django import forms
from .models import Topic

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = {'text': ''}

まずは form module と Topic model をインポートします。TopicForm という名前のクラスと作り、forms.ModelForm クラスから継承します。

class Meta:

このクラスの中にある Metaクラスはどのモデルがフォームのベースになるのか、どのフィールドがフォームに含まれるのかをDjango に教えます。

model = Topic fields = ['text']

Topic モデルからフォームを作り、text フィールドだけを追加します。

labels = {'text': ''}

Django に text フィールドにラベルを作成しないように伝えています。

new_topic URL

新しいトピックを入力するためのURLパターンを作成します。URL はできる多で簡単なものがいいので、今回はhttp://localhost:8000/new_topic/を使用します。今までの手順通り、最初は learning_logs/urls.py に new_topic ページのURL パターンを追加します。

# learing_logs/url.py

#--省略--
urlpatterns = [
    #--省略--
    # 新しいトピックを追加するページ
    url(r'^new_topic/$', views.new_topic, name='new_topic'),
]

このURLパターンは view function の new_topic() にリクエストします。

new_topic() function

new_topic() では2つの状態を対処しなければなりません。空欄とデータが入力されたときです。トピックを追加した後は、topics ページにリダイレクトするようにします。

# views.py

from django.shortcuts import render       #すでにある
from django.http import HttpResponseRedirect
from django.urls import reverse

from .models import Topic           #すでにある
from .forms import TopicForm

--省略--
def new_topic(request):
    """新しいトピックを追加"""
    if request.method != 'POST':
        # データが入力されてない、空欄
        form = TopicForm()
    else:
        # POSTデータが送信された
        form = TopicForm(request.POST)
        if form.is_valid():
            new_topic.save()
            return HttpResponseRedirect(reverse('practicing_logs:topics'))

    context = {'form': form}
    return render(request, 'practicing_logs/new_topic.html', context)

入力した後にリダイレクトするために、HttpResponseRedirect をインポートしました。reverse() はURLパターンからURLを決定します。また、TopicForm もインポートします。

GET と POST リクエスト

ウェブアプリを作るときには GETリクエストPOSTリクエストの2種類があります。GETリクエストはサーバーからデータを読むだけの時に使います。POSTリクエストはユーザーがデータを送信する必要があるときに使用します。今回はトピックを追加するので POSTリクエストを使用します。

new_topic() は request オブジェクトをパラメーターに取ります。ユーザーがこのページをリクエストした時に、ブラウザは GET リクエストを送ります。ユーザーがフォームに入力し送信した時に、ブラウザはPOSTリクエストを送信します。フォームが空欄だった場合は、GETリクエストとなります。

if request.method != 'POST':

リクエストが GET か POST のどちらであるかを決定します。POST ではない、つまりおそらくGETである場合は 空欄のフォームを返します。form という名前の TopicForm のインスタンスを作り、conext に入れて template に送ります。

form = TopicForm(request.POST)

リクエストが POST で else が実行された場合、TopicForm のインスタンスを作り、ユーザーが入力しrequest.POST に保存されたデータを送ります。

if form.is_valid():

送信されたデータが正しいフォーマットか確認が取れるまでデータベースに保存することはできません。is_valid()すべての必須の項目が入力されているかどうかを確認します。(デフォルトですべての項目は必須となっています。)また、フォーマットが正しいかどうかも確かめます。例えば、models.py で text は200文字以内と定義しました。これが満たされているかをチェックします。これらの作業を自動的に行ってくれます。

new_topic.save() return HttpResponseRedirect(reverse('practicing_logs:topics'))

すべての確認が取れたら、save() が実行され、入力されたデータがデータベースに保存されます。そのあとは reverse() を使って topics ページのURLを取得し、HttpResponseRedirect() に送られます。topics ページでユーザーが入力したトピックが見えるはずです。

new_topic template

フォーム画面を表示させるための new_topic.html を作成します。

# new_topic.html

{% extends "practicing_logs/base.html" %}
{% block content %}

<p>Add a new topic:</p>
<form action="{% url 'practicing_logs:new_topic' %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">add topic</button>
</form>

{% endblock content %}

またまた、base.html を使用しました。

<form action="{% url 'practicing_logs:new_topic' %}" method='post'>

HTMLフォームを定義します。action は入力されたデータをどこに送るのかをサーバーに伝えます。今回は、 view function の new_topic() です。送信の仕方は POST です。

Django は template tag の {% csrf_token %}を使ってサーバーへの侵入の攻撃を防ぎます。Django ではフォームを作成するのに、template variable {{ form.as_p }} を使用します。自動的にすべてのフィールドを作成してくれます。as_p はフォーム要素を段落形式で並ばせるように指示します。これはフォームをきれいに見せるシンプルな方法です。ただ、Django は送信ボタンは自動的に作らないので、こちらで定義する必要があります。

new_topic ページをリンクさせる

topics ページから new_topics ページに飛べるようにリンクを貼り付けます。

# topics.html

{% extends "learning_logs/base.html" %}
{% block content %}
 <p>Topics</p>
 <ul>
 --省略--
 </ul>

 <a href="{% url 'practicing_logs:new_topic' %}">Add a new topic:</a>
{% endblock content %}

今 topics ページを見ると new_topic ページへのリンクが追加されているはずです。new_topic ページは下のようになっているはずです。

f:id:kendamaaa:20200427065133j:plain

超初心者用: Djangoでウェブアプリを作る ④追加ページの作成

前回はホームページを作りました。

今回はそれ以外のページを作っていきます。トピックのリストのページとそれぞれのトピックの Entry のページの2ページを作ります。それぞれのページにURLパターンをつけ、view を書き、template を書きます。

その前にすべてのテンプレートで使う base template を作ります。

テンプレートの継承

ウェブサイトを作るとき、どのページでも必ず存在する要素があります。例えばロゴとかナビゲーションバーとか。このコードを枚描くのではなく、base template として繰り返すことができます。このようにすることで、時間がだいぶ節約できます。

Parent template

index.html と同じ階に base.html という template を作成します。このファイルがすべてのページに適応されることになります。つまり、すべての template が base template を受け継ぎます。今回は、タイトルだけです。タイトルにホームページへのリンクをつけましょう。

<!--base.html-->
<p>
    <a href="{% url 'practicing_logs:index' %}">Practicing Log</a>
</p>
{% block content %}{% endblock content %}

リンクを生成するには、tamplate tag の {% %} を使います。url 'practicing_logs:index' は practicing_logs という app_name の practicing_logs/urls.py 内の index という名前のURL を意味しています。

URLを直接入力するのではなくこのようにかくことで、URLを変更したときに修正する必要がなくなります。つまり URLを変えるときは、urls.py を変えるだけでOKです。

すべてのページがこの base template を持つことになるので、もうタイトルの記入は必要ありません。この template を引っ張ってくるだけです。

{% block content %}{% endblock content %}

これは content と呼ばれ、ほかページの内容がここに追加されます。

Child template

base template ができたので、ホームページを書き換えましょう。

{% extends "practicing_logs/base.html" %}
    {% block content %}
        <p>Practicing Log helps you keep track of your pracitcing, for any topic you're placticing about.</p>
    {% endblock content %}

{% extends "practicing_logs/base.html" %}

元の index.html と比較すると、タイトル部分が base template に置き換わっています。ほかの template を受け継ぐには、{% extends %} tag が必要になります。今回は practicing_logs 内の base.html を受け継ぐため、 practicing_logs/base.html と表記します。

{% block content %}

先ほど見かけた content が早速登場しました。parent template から受け継いでいない要素はすべてこの中に入ります。今回は文章が入ります。最後には {% endblock content %} tag で閉じます。

このように、すべてのページにおいて変わらない要素を別の template で作ることで、子ページには呼ぶだけで利用することができます。また、編集をしたいときには base template のみ編集するだけで全ページに適応されます。

Topics pageを作成する

では追加ページを作る本題に入っていきます。まずはトピックページです。このページでは、ユーザーが入力したすべてのトピックをリストとして表示させます。つまりデータを扱うことになります。

topics URL pattern

まずはトピックページの URL を定義します。できるだけシンプルなものにしましょう。今回は topics にします。つまり、http://localhost:8000/topics/です。practicing_logs/urls.py を変更します。

#practicing_logs/urls.py
urlpatterns = [
    # Home page
    url(r'^$', views.index, name='index'),
    #-------------------ここから下を追加---------------------
    #topics のリンク
    url(r'^topics/$', views.topics, name='topics'),
]

「r'^topics/$」 は topics/ のURL を表しています。つまり base URL + topics/ となる URL の時に適応されます。URL が一致するときは views.py の topics() を呼びます。

Topics view

topics() function はデータベースからデータを取得し、template に送る必要があります。

# views.py
from django.shortcuts import render  <-- すでにある
from .models import Topic

def index(request):          <-- すでにある
   --省略--

def topics(request):
    """すべての topics を表示する"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics': topics}
    return render(request, 'practicing_logs/topics.html', context)

from .models import Topic

まずは必要となるデータに基づいたモデルをインポートします。

def topics(request):

topics() function は request parameter が必要です。

topics = Topic.objects.order_by('date_added')

ここでTopicを通すことでデータベースからデータを取得します。date_added attribute によって追加された順に topics に保存していきます。

context = {'topics': topics}

template に送るための context を定義します。context は dictionary で、key がデータを呼ぶための名前で、value がデータ自身です。この場合、1つの key に対して複数の value が存在し、それらは set として保存されています。

return render(request, 'practicing_logs/topics.html', context)

render() を使って context の内容を practicing_logs/topics.html に送っています。

Topics template

トピックページの template は context のデータを受け取っています。だから、ここで使うことができます。index.htmlと同じ階にtopics.htmlを作成してください。

# topics.html
{% extends "practicing_logs/base.html" %}
{% block content %}
    <p>Topics</p>
        <ul>
            {% for topic in topics %}
                <li>{{ topic }}</li>
            {% empty %}
                <li>No topics have been added yet.</li>
            {% endfor %}
        </ul>
{% endblock content %}

まずは {% extends %} tag を使って、このページにタイトルを付けます。{% block content %} からこのページ独特の内容を書き込みます。リストとして表示されるように unordered list の

    を作ります。

    {% for topic in topics %}

    ここでは for loop を使って context 内の topics をループします。ループの終わりには{% endfor %}と書きます。

    ループの中では topic を順番に表示させます。{{ topic }}と書くことで key である topic の value が表示されます。

    {% empty %}

    これは if節でもしトピックがなかったらこれ以降を実行します。今回はトピックがないというメッセージを表示させます。

    では、ナビゲーションとして base template にトピックページを追加しましょう。

    # base.html
    <p>
        <a href="{% url 'practicing_logs:index' %}">Practicing Log</a>
        <a href="{% url 'practicing_logs:topics' %}">Topics</a>
    </p>

    Topics

    practicing_logs という名前の app_name から topics という名前の URL パターンを示しています。

    トピックページはこんな感じになっているはずです。

    f:id:kendamaaa:20200425163349j:plain

    それぞれのTopicページ

    次は、トピック全体ではなく1つのトピックに焦点を置いたページを作っていきます。このページにふくまれるのは、トピック名とそのトピックの entries です。再び URLパターンを作成していきます。さらに先ほど作ったトピックページも編集し、これから作るぺージに飛べるようにします。

    Topic URL pattern

    これから作るトピック専用ページの URLパターンは今までのURLパターンと少し異なります。トピックの id を利用します。例えば、id が1であるChess トピックページを見たいときは、http:// localhost:8000/topics/1/となります。

    # practicing_logs/urls.py
    urlpatterns = [
        #--省略--
        # トピックの詳細ページ
        url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
    ]

    r'^topics/(?P<topic_id>\d+

    r は string を raw string として扱うように示しています。(?P<topic_id>\d+ の「\」までの中身はトピックの id 意味します。

    このパターンに合うURL が見つかったときに、id とともに topic() function を呼びます。

    Topic view

    topic() function はトピック名とそれの基づいたすべての entries のデータを取得します。

    # views.py 
    --省略--
    def topic(request, topic_id):
        """1つのトピック名とそれのすべての entries を表示"""
        topic = Topic.objects.get(id=topic_id)
        entries = topic.entry_set.order_by('-date_added')
        context = {'topic': topic, 'entries': entries}
        return render(request, 'practicing_logs/topic.html', context)

    今回は request だけではなく topic_id という parameter を使います。topic_id は (?P<topic_id>\d+) で取得したものです。

    topic = Topic.objects.get(id=topic_id)

    get() を使ってその id のトピック名を取得します。

    entries = topic.entry_set.order_by('-date_added')

    そのトピックに関連した entries を時間の新しい順「-」に取得します。こうすることで最新情報を一番上に表示することができます。

    取得したデータを context dictionary に保存します。最後に template topic.html に context を送ります。

    Topic template

    この template ではトピック名と entries を表示させる必要があります。さらに entry が1つもなかった場合、その旨も知らせなければなりません。

    # topic.html
    {% extends 'practicing_logs/base.html' %}
    {% block content %}
        <p>Topic: {{ topic }}</p>
        <p>Entries:</p>
        <ul>
            {% for entry in entries %}
                <li>
                    <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
                    <p>{{ entry.text|linebreaks }}</p>
                </li>
            {% empty %}
                <li>
                    There are no entries for this topic yet.
                </li>
            {% endfor %}
        </ul>
    {% endblock content %}

    まずはいつも通り、base template を組み込みます。そして block content が始まることを知らせます。

    Topic: {{ topic }}

    一番上にトピック名を表示させます。{{ topic }} は context から引っ張ってきます。

    {% for entry in entries %}

  • {{ entry.date_added|date:'M d, Y H:i' }}

    {{ entry.text|linebreaks }}

  • for loop を使って entries の日付と内容を1つずつ表示させていきます。「|」は表示方法のフォーマットを定義しています。

    {% empty %}

  • There are no entries for this topic yet.
  • entry がない場合はこちらが実行されます。最後に for loop を閉じて終わり。

    Topicsページにリンクをつける

    個々のトピックページを見るためには、全体のトピックページにリンクをつけなければなりません。

    #--省略--
        {% for topic in topics %}
            <li>
                <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
            </li>
        {% empty %}
    #--省略--

    topic の名前によって適切な URL を使わなければなりません。URL パターンは topic_id を必要としているので、URL tag に topic_id を付け加えました。今、http://localhost:8000/topics/1/ に飛ぶと、Chess の詳細が見えるはずです。

    f:id:kendamaaa:20200426050855j:plain

超初心者用: Djangoでウェブアプリを作る ③ホームページを作る

Djangoでページを作るときには大きく分けて3つの段階あります。

  1. URLの定義
  2. views を書く
  3. templates を書く

まずはURLを定義します。そのURLを見てDjangoにどのサイトを表示させるのかを教えます。

次に、それぞれのURLが特定のviewにマップします。 view function はページに必要なデータを操作します。その view function の中でページを作成する template が呼ばれます。

この仕組みを理解するために簡単なホームページを作ってみましょう。

URLをマッピングする

まずURLをマッピングするのはホームページが一番最初です。

ベースとなるhttp://localhost:8000/をユーザーが入力したときにホームページを表示させるようにします。

まずは practicing_log フォルダの urls.py を開いてください。

from django.conf.urls import url, include
from django.contrib import admin  <--すでにある
     
urlpatterns = [
    url(r'admin/', admin.site.urls),  <--すでにある
    url(r'', include('practicing_logs.urls')),
]

from django.conf.urls import url, include

プロジェクトとadmin siteのURLを管理する function と modules をインポートします。

urlpatterns = [ ]

この中にこれから定義するURLを入れていきます。

url(r'', include('practicing_logs.urls'))

ここでは practicing_logs modules を追加しています。


デフォルトのurls.py ファイルは practicing_log フォルダの中にあります。次は、practice_logs フォルダの中に 2つ目のurls.py を作成する必要があります。

#practicing_logs/urls.py
         
"""practicing_logs のためのURLパターンを定義する"""
from django.conf.urls import url
from . import views
         
app_name = 'practicing_logs'
urlpatterns = [
    # Home page
    url(r'^$', views.index, name='index'),
]

urls.py が2つあるので、簡単に区別すことができるように先頭にコメントをしておきましょう。

from django.conf.urls import url

URLs を view にマッピングするために必要な url function をインポートします。

from . import views

views module をインポートします。「.」はこのファイルと同じ階級の中から探すという意味です。

app_name = 'practicing_logs'

practicing_logs app のURLをほかの app と区別するためにこのように名前付けします。今は1つの app しかありませんが、数が増えたときに便利さがわかります。

urlpatterns = [ ]

ここでは、practicing_logs app の中の個々のURLを定義します。

url(r'^$', views.index, name='index'),

url function は3つの arguments を持っています。1つ目は、regular epression です。DjangoはURLが与えられたときに、ここを見てどのページがリクエストされているのかを決めます。

r'^$' を詳しく見ていきます。「r」は string を row string として解釈することを意味しています。「^」は string の初めの位置を知らせています。「$」は終わりを示しています。今回は、^と$の間には何もありません。つまりホームページとなる、 http://localhost:8000/を表しています。これ以外のURLではこのページは表示されず、エラー画面が返ってきます。

2つ目の argument はどの view function を実行するのかを特定しています。URLが合致した時に、views.index を実行します。3つ目はこのページの名前を定義します。今回は「index」いちいちURLを書くのではなく、この名前を使います。

views.py に記入

view function はリクエストされた情報を受け取り、ページを作成するためのデータをブラウザを作るための templates に送ります。

practicing_logs 内の views.py に書き込んでいきます。

#views.py   

from django.shortcuts import render

def index(request):
   """Practicing Log のホームページ"""
   return render(request, 'practicing_logs/index.html')

先ほど定義したURLと入力されたものと一致するときに、views.py の中から index() function を探します。今回は何のデータも送る必要もないので render() を呼ぶだけです。render() は request とページを作る template の arguments を持っています。次は template を作っていきます。

Templateを書く

template とはウェブページの構造を作るものです。ここでページがどのような見た目になるかを決めます。また、view から送られてきたデータを自由に画面に表示させることができます。今回は何のデータも送ってないのでシンプルです。

practicing_logs フォルダの中に templates フォルダを作ります。次に、templates フォルダに practicing_logs フォルダを作ります。practicing_logs が2つもあるのは変な感じがしますが、多くの app になったときのためにこのようにします。新しく作った practicing_logs フォルダ内に index.html を作ります。

<!--index.html-->
<p>Practicing Log</p>
<p>Practicing Log helps you keep track of your practicing, for any topic you're practicing about.</p>

これはHTMLファイルで、これをもとにホームページを作成します。今、http://localhost:8000/ に飛ぶと、今まで見たデフォルトのDjangoページではなく、今作ったページを見ることができます。Base URL の後に何も記述されていないということは、r'^$' ということです。これが一致するところから views.index() を呼び、その中の index.html を返すことでこのページが表示されました。

簡単にかくと、

  1. http://localhost:8000/
  2. r'^$'
  3. views.index()
  4. index.html
  5. ホームページに表示

f:id:kendamaaa:20200425123335j:plain

こんなシンプルなページ1つを作るのにかなり複雑でしたが URL, views, template に分けて作ることができました。規模が大きくなればなるほど、この利益を実感することができます。例えば、データスペシャリストは models に、プログラマーは view に、ウェブデザイナーは templates に焦点を当てます。

超初心者用: Djangoでウェブアプリを作る ②Appを開始する

前回の続きです。

Appをスタートさせる

Django Projectはそれぞれの機能を持ったappsの集まりです。

現段階では、今回のプロジェクトのほとんどの機能を担う1つのappを作成します。いずれユーザーアカウントを管理するための app も追加します。

ちなみにこの段階では以前起動させたサーバーをストップさせないでください。

新たな terminal を開いて以下を実行してください。この時、manage.py が入っているフォルダに焦点を当ててください。

cd practicing_log

で移動できると思います。

python manage.py startapp practicing_logs

startapp appName でDjangoに app を作るように命令します。今回は practicing_logs という名前にしました。

実行後はpracticing_logs フォルダが追加されているはずです。重要なファイルは、models.py, admin.py, views.py です。

f:id:kendamaaa:20200424081001j:plain

Modelsを定義する

まずはデータについて考えてみましょう。それぞれのユーザーは Practicing_log 内に トピック(topics) を作ります。さらにそれぞれの内容(entry) はトピックに紐づいています。また、それぞれの内容に記入時間を付け加えます。

models.pyを開いてください。

models.py は自動的に追加されたファイルです。そして、独自の models を作ることができます。Model は Djangoに app に保存されてあるデータをどのように使うかを教えます。ちなみに、モデルは Class です。

# manage.py
class Topic(models.Model):
    """ユーザーのトピック"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """textを返す."""
        return self.text

Model を継承しているTopic という Class を作りました。text と date_added の attributes を持っています。

text の attribute は CharField で、文章によって形成されたデータです。CharField を使うとき、文字数制限をしなければなりません。今回は200文字です。

date_added attribute は DateTimeField で、日にちと時間のデータです。auto_add_now=True は自動的に現時刻をセットするように命令しています。

Djangoに、トピックの情報を表示するとき、デフォルトとしてどの attribute を使うのかを知らせなければなりません。str() は text attribute のデータを返します。

Modelsをアクティベートする

作成したモデルと使うには、プロジェクトに app を追加しなければなりません。setting.pyを開きます。すでに、installed_apps の中にはたくさんの apps がインストールされています。ここに作った app を追加します。下のようになります。

#setting.py
--省略--
INSTALLED_APPS = (
    --省略--
    'django.contrib.staticfiles',

   # My apps
    'practicing_logs',
)
--省略--

practicing_logs を追加しました。

次は、Djangoにデータベースを修正し、トピックのデータも保存できるように命令しなければなりません。terminal から実行してください。

python manage.py makemigrations practicing_logs

makemigrations はデータベースをどのように変更するかを考えるように命令します。裏でいろいろ起こって migration file が作成されます。

これを適応させるために以下を terminal にて実行します。

python manage.py migrate

ちゃんと動けば最終行に、

Applying practicing_logs.0001_initial... OK

と表示されるはずです。

管理するデータを変更するときはいつも、3つのステップを実行します。

  1. models.py を修正
  2. makemigrations を呼ぶ
  3. migrate する

Django Admin Site

app のモデルを定義した時、admin site を使うことで簡単に編集したりすることができます。このサイトは管理者だけがアクセスすることができるため、ほかのユーザーからは見えません。admin site を設定します。

Superuserの設定

Djangoでは特別な権利を持ったユーザーを作ることができます。これを superuser と呼びます。ユーザーの行動を操作したり、制限したりできます。つまり、そのサイトの管理者です。

superuser を作るには、以下を terminal にて実行します。

python manage.py createsuperuser

ユーザー名、メールアドレス、パスワードの設定を要求されます。メールアドレスは省略することができます。これらは admin site に入るために必要になります。

※admin site からパスワードを見ることはできません。ハッシュというものに変換されて保存されます。

ModelをAdmin siteに登録

admin site には User や Group などのモデルが自動的に登録されています。ただ自らが作ったモデルは手動で追加しなければなりません。

models.py と同じ階にある admin.py を開いてください。Topic もでるを登録するには以下のように書きます。

    #admin.py
    from django.contrib import admin    #すでにある
    from practicing_logs.models import Topic 
    admin.site.register(Topic) 

まず Topic のモデルをインポートします。そして admin.site.register() を使って admin site に登録します。

今、http://localhost:8000/admin/ にアクセスしてみましょう。/admin/をつけるのを忘れないように! ユーザーネーム、パスワードを入力するとログインすることができます。このページではユーザーを追加したり、編集したりできます。また、Topic もあるはずです。

f:id:kendamaaa:20200424091807j:plain

Admin siteからTopicを追加する

admin site にトピックモデルが追加で来たので、具体的なトピックを追加してみましょう。ここでは、「Chess」と「Rock Climing」を追加します。

f:id:kendamaaa:20200424092407j:plain

Entry Modelを定義する(models.py)

Chess と Rock Climbing についての内容を保存するための Entry モデルを定義します。それぞれの Entry は特定のトピックに結び付いています。この関係性を many-to-one relationship と呼びます。1つのトピックにたくさんの Entry が付くことができる。

Entry モデルを定義します。

#models.py
from django.db import models
class Topic(models.Model):
    --省略--
class Entry(models.Model):
    """特定のトピックに関する内容"""
    topic = models.ForeignKey(Topic, on_delete=models.PROTECT)
    text = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = 'entries'
    def __str__(self):
        """初めの50文字まで表示"""
        return self.text[:50] + "..."

Entry クラスも Model を継承します。

topic attribute は ForeignKey で、別のデータベースから参照します。このコードが特定のトピックと結びつけます。それぞれのトピックにKey や ID が生成されます。例えば、Chess は1、Rock Climbing は2。つながりを作るとき、このKeyと結びつけます。

text attribute は TextField で文字列です。ここでは文字制限をしていません。最後は時間です。Topicでも説明したので省略します。

Meta クラスは複数の Entry を参照する必要があるときに Entries を使用するように指示する特別な attribute を設定することができます。

str() は初めの50文字のみを返します。

Entry ModelをMigrateする

新しいモデルを追加したので、データベースに反映しなければなりません。この工程は2度目になります。

以下を terminal でそれぞれ実行します。

python manage.py makemigrations practicing_logs
python manage.py migrate

0002_entry.py が生成されるはずです。

Entry ModelをAdmin siteに登録

# admin.py
from django.contrib import admin
from practicing_logs.models import Topic, Entry  #<-ここ
admin.site.register(Topic)
admin.site.register(Entry)   #<-ここ

もう一度 admin site に行って確認してみてください。追加されているはずです。

EntryからそれぞれのTopicを選び、Entryを追加する。

f:id:kendamaaa:20200424094906j:plain

次回

次回は超シンプルなホームページを作ります。

超初心者用: Djangoでウェブアプリを作る ①必要なものを整える

今回のプロジェクトでは Jetbrains社のPycharmというIDEを使って、簡単なウェブアプリを作ります。

今までにDjangoを触ったことがない人や開発環境の構築すらしたことがない人超初心者が対象です。

最近のウェブサイトはリッチ(rich)アプリケーションといって、いろんなことができます。Pythonはそれを可能にするツールがたくさん入っています。今回はDjangoというフレームワークを使っていきます。

バージョン

  • Pycharm 3.7.7
  • Django 3.0.3

この時点で必要なもの

  • パソコン
  • Pycharm

必要なものをインストールする

ここでインストールするのはAnacondaのみ Anaconda とは簡単に説明すると、Python を使うにあたって便利なものを最初から集めてくれたセットです。 このプロジェクトで必要になってくるセットがすべて入ってるので、かなり便利です。

それでは簡単にダウンロードしていきます。

こちらのサイトに飛んで、ダウンロードするだけ。

ボタンを押して、説明に従うだけなので詳しくは説明しません。

f:id:kendamaaa:20200424070030j:plain

Ⅰ. プロジェクトをスタートさせる

プロジェクトを作る。(バーチャルはConda)

Djangoを使用するときはまず仮想環境を設定します。仮想環境とはパッケージをインストールしたり、ほかのパッケージからそれらを孤立させることができる場所です。

それではPycharmを開いてください。

「Create a New Project」を選びます。

①今回は practicing_log という新しいdirectryを作成します。言い換えると、practicing_log というファイルを作ります。

②仮想環境に Conda を選択します。

③Create

f:id:kendamaaa:20200424071008j:plain

無事に作成できたら下のような画面になります。

f:id:kendamaaa:20200424071510p:plain

Djangoをインストール

project interpreterからDjangoの追加します。

File -> Setting -> Project: practicing_log -> Project Interpreter -> +

f:id:kendamaaa:20200424071939j:plain

Djangoを検索してインストールします。

f:id:kendamaaa:20200424072002j:plain

Djangoでプロジェクトを作成

Pycharm内のterminalを開いて、Djangoに新しいプロジェクトを作成するように命令します。

django-admin startproject practicing_log

上のコマンドを実行すると、フォルダー内にいろいろと追加されているのが確認できると思います。

f:id:kendamaaa:20200424072449j:plain

データベースを作成

Djangoはプロジェクトに関するほとんどの情報をデータベースに保存するため、Djangoが使えるデータベースを作成する必要があります。Terminalにて以下のコードを実行してください。

cd practicing_log #ファイルに移動する
python manage.py migrate

f:id:kendamaaa:20200424073118j:plain

データベースを修正するときは常に統合(migrate)をします。一番最初はデータベースを作成します。

データベースファイルが作成されますが、今後特に気にかける必要はありません。

サーバーを起動

python manage.py runserver

サーバーを起動させることで、作っているプロジェクトを見ることができます。ほかの人は見ることはできません。

f:id:kendamaaa:20200424073754j:plain

URLを開くと、このように表示されるはずです。サーバーをストップさせたいときは CTRL-C を押してください。

f:id:kendamaaa:20200424074556j:plain