ICT関連の学習備忘録

ICTの学習をアウトプットするワークブックサイト

ICT関連の学習備忘録

2019/09/04:ECサイト作成(2)-商品詳細ページ、編集ページ-

ネットショップ的な何かを作るプロジェクト、前回のおさらい。
プロジェクトを作成
 venveからstartprojectとstartapp

MySQLとの接続設定・データベース作成
 manage.pyでpymysqlをインポート、settings.pyでホストやDB接続情報等々、
 models.pyでデータベースクラスの作成、これらを元に、
 makemiggration、migrateでデデータベース作成。

URLの設定(urls.py)で商品一覧を表示するテンプレートを指定
 viewsをインポートして、URLのパス設定
 現状は、管理ページ(admin)と、商品一覧(product_list)のみ。

views.pyでレスポンスの設定。
 TemplateResponseをインポートして、リクエストに対して
 /catalogue/product_list.htmlを返す設定。

レスポンスで指定されているproduct_list.htmlの中で
商品一覧を表示

までをやった、らしい。

そこからの続きです。

商品一覧ページから、商品名をクリックしたら
商品の詳細ページにいくようにする。

個人的にリンク(html側)を作ってからのほうが流れがつかみやすいので、
まずはproduct_list.htmlの商品リストにURLタグを使ってリンクを貼り付ける。
#shop/templates/catalogue/product_list.html
{% for product in products %}
  <li>({{ product.category.name}})<a href="{% url 'product_detail' product.id %}">
        {{ product.name }}:{{ product.price }}円</a></li>
  {% endfor %}
</ul>
{% endblock%}
まぁ、アンカータグ付けるだけなんだけどね。
{%%}の中身は、urlタグでurls.pyのurlpatternsから引っ張ってきている(と思う。)

んで、そのurls.pyの中身も少し変えて、
products/(product_id)/へのリクエストが来た場合のパスを記述
#mysite/urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('products/<int:product_id>/', shop.views.product_detail,
        name='product_detail'),
    path('',shop.views.product_list, name='product_list'),
]
さっきのURLタグの'product_deatil'はこれのnameを指してる(気がする。)
次の道標としてはviewsのproduct_detailメソッドが宛先になるので、
views.pyの中にレスポンス用のメソッドを追加する。
#shop/views.py
from django.http import Http404
(中略)
def product_detail(request, product_id):
    try:
        product = Product.objects.get(id=product_id)
    except Product.DoesNotExist:
        raise Http404
    return TemplateResponse(request, 'catalogue/product_detail.html',
    {'product':product})
importで404入れてるけどこれは、存在しない商品IDに
アクセスした場合のエラーページ用。
通常経路で、products/**/で来た場合は、product_detail.htmlのテンプレートを
開くようにする。開くのと一緒に単一の商品データをもたせる。
product = Product.objects.getでID一致の商品データのみをレスポンスするようにしてる。

で、最後に単一商品データを受取って表示するテンプレートを用意。
#shop/templates/catalogue/product_detail.html
{% extends 'catalogue/base.html' %}

{% block title %}商品詳細{% endblock %}

{% block content %}
<h3>{{ product.name }}</h3>
 {{ product.price }}円
{% endblock %}
流れを自分なりの解釈でイメージしたらこんな感じになった。
なんとなくでもイメージしないとただの模写みたいになっちゃうから・・・汗

商品編集ページを用意する

※現時点ではまだ誰からでも更新出来てしまいます(ザルw)
順番は何でも良いんだけど、
手続き型プログラミングしか出来ない人なので
流れが見えるような形(自分的に)で勧めます。

1.商品編集用のURLを用意する。
#mysite/urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('products/<int:product_id>/', shop.views.product_detail,
        name='product_detail'),
    path('',shop.views.product_list, name='product_list'),
    path('products/<int:product_id>/edit/', shop.views.product_edit,   #**/product/edit/を追加。
        name='product_edit'),
]
先程の詳細ページとほぼ同様。int:product_idから更に入り込んだだけ。
からのviews.pyのproduct_editへリクエストがいくのでレスポンス用のメソッドを記述

2.商品編集URLからのリクエストを受け取りレスポンスを返すメソッドを追加する
#shop/views.py
from django.http import Http404, HttpResponseRedirect #404の他、HttpResponseRedirectを追記
from shop.forms import ProductEditForm  #まだ作ってないけどformsモデルをインポート

def product_edit(request, product_id):
    try:
        product = Product.objects.get(id=product_id)    
    except Product.DoesNotExist:
        raise Http404

    if request.method == 'POST':        #編集画面で変更ボタンが押された場合の処理
        form = ProductEditForm(request.POST, instance=product)  
        if form.is_valid():  #バリデート。入力値にエラーが無いかチェック
            form.save()
            return HttpResponseRedirect(reverse('product_detail',
                                        args=(product.id,)))
    else:
        form = ProductEditForm(instance=product)
    return TemplateResponse(request, 'catalogue/product_edit.html',
                            {'form': form, 'product': product})
リクエストの受け取り方は詳細ページと同様。
ifの処理
 編集画面から変更ボタンが押された場合の動作がif~=='POST'の方で
 ModelFormはinstance=という引数を受け取れ、
 引数を指定した場合、渡されたオブジェクトの値を変更する。
 上記の場合は、productの値をPOSTの値に変更している。
 その後、valid()で値に誤りが無いかの確認をし、form.save()で上書き処理をしている。
elseの処理
 商品詳細ページからproduct_edit.htmlに来た場合はPOSTが空の為、
 editページを開き、フォーム内に初期値として現在の値を入れる処理として
 instance=productでオブジェクトの値を変更後テンプレートへ受け渡している。(多分)

3.Formのモデルを作る(ModelForm) ProductEditFormクラスを作り、その中で使用するカラムを定義する。
#shop/forms.py
from django import forms
from shop.models import Product

class ProductEditForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = (
            'name',
            'price',
        )
以上。

4.商品詳細ページに編集のリンクを埋め込む。
これも、詳細ページ作ったと同様にurlタグで引っ張ってくるだけ。
#shop/templates/catalogue/product_detail.html
{% extends 'catalogue/base.html' %}

{% block title %}商品詳細{% endblock %}

{% block content %}
<h3>{{ product.name }}</h3>
 {{ product.price }}円
 <a href="{% url 'product_edit' product.id %}">編集</a>
{% endblock %}
5.変種ページの作成。
先程POSTで待受けるメソッドをviews.pyに記述したので、
formのメソッドも当然postでリクエストを飛ばします。
formタグの間にCSRF対策も埋め込んでおきます。
#shop/templates/catalogue/product_edit.html
{% extends 'catalogue/base.html' %}
{% block title %}商品編集{% endblock %}
{% block content %}
<form method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="変更">
</form>
<a href="{% url 'product_detail' product.id %}">商品画面に戻る</a>
{% endblock %}
6.今更だけどWEBページの枠組み作成
しれっと{% block ~~ %}って入れてたけど一応記述しておく
まー枠組みよね・・ブロックっていうか、でかい変数見たいな感じ。
ベースのHTMLに動的箇所を別ファイルで変更できるように目印を付ける。
#shop/templates/catalogue/base.html
<!DOCTYPE html>
<html>
<head>
    <meta charaset="UTF-8">
    <title>PySHOP - {% block title %}{% endblock%}</title>
</head>

<body>
    <a href="{% url 'product_list' %}">商品一覧</a>
    <hr>

{% block content %}
{% endblock %}
</body>
</html>
これが、イメージするまんまの「テンプレート」って気がする。
{% block *** %}{% endblock %}
の間を別のファイルが呼び出して自ら埋め込まれる感じ(謎)
・・自分で使い方分かってるから詳細はいいや←。

今回はここまで!


前のページへ戻る