詳細画面と一覧画面の流れ


URLの一部をView関数に渡す
商品詳細画面ではURLに取得したい商品のID値を含みます。
このID値を元に商品を取得したいので、URLからView関数に渡す必要があります。
DjangoのURLsでは、URLに <> 山括弧をつけて変数として取れます。
たとえば今回の 'products/<int:product_id>/' のようにすると <int:product_id> の部分に整数(int)の値が渡されるとView関数に渡されます。

この変数はView関数の引数に渡されます。
たとえば def product_detail(request, product_id): の product_id はURLsの <int:product_id> から取得された値です。
View関数の request 以降の引数に URLsに指定したキーワード名(今回は product_id)と同じ名前の引数が指定されているとその引数に値が代入されます。
URL /products/1/ にアクセスされた場合、def product_detail(request, product_id): の product_id には 1 が入ります。



Product.objects.get
objects.get は単一のオブジェクトを取得する際に使います
(objects.filter は複数のオブジェクトを取得します)。

Product.objects.get(id=product_id) は「id の値が product_id の商品を取る」ということです。
/products/1/ にアクセスされた場合は id の値が 1 の商品を取得できます。

get についての ドキュメントも参考にしてください



Product.DoesNotExist
objects.get はオブジェクトが取得できない場合と複数のオブジェクトが取得できた場合に例外を送出します。
同じID値を持つオブジェクトが2つ以上になることは絶対にないので、ここでは「期待したID値の商品が存在しない」場合の例外処理が必要です。
期待したオブジェクトが存在しない場合 Product.DoesNotExist が送出されます
(たとえば MyModel モデルの場合は MyModel.DoesNotExist)。

try:
    product = Product.objects.get(id=product_id)
except Product.DoesNotExist:
    print("ID値が一致する商品がない場合")
raise Http404


商品が存在しない場合は 404 画面を表示します。
404画面というのは「ページが存在しません」「NotFound」のような画面のことを言います
(どこかで一度は見たことがあるでしょう。 http://google.com/aaaaaaaaaa のような「存在しない」ページです。アクセスすると "404", "not found" と表示されます)



Djangoでは raise Http404 という例外をView関数内から送出することで、自動的に404画面が表示されます。
product_detail View関数では Product.objects.get(...) で商品が取れない場合に raise Http404 として404画面を表示しています。

Djangoに限った話ではなくHTTPのレスポンスにはこの「ステータスコード」が定義されていて、
ステータスコードごとに「どういった内容のレスポンスか」を表しています。

以下がよく使われます

200: リクエストが成功して要求通りの内容が返されている
ブラウザーで普段Webサイトをみているときなどは、ステータスコードが200でレスポンスが返されています
特に指定がない場合、Djangoの TemplateResponse はステータスコードを 200 で返します
404: 見つからなかった。要求されたデータやリソースがなかった場合
商品がない場合に raise Http404 としているのはこのためです
500: サーバーの内部でエラーがあった
興味がある人は WikipediaのHTTPステータスコード
を読んでください。



{% url ... %}
HTML上でリンクを作るには <a> タグを使います。 href="/" のようにアクセス先のリンクをしていします。
たとえばトップ画面(/)へのリンクは <a href="/">商品一覧</a> のように書けます。

ただ「商品一覧にアクセスするURLは / だ」と覚えておくのは面倒ですし、
ec/urls.py を少し変えただけでテンプレートを全て直すのも面倒です。
Djangoでは画面へのリンクを取得する仕組みがあるので使いましょう。
テンプレート内で使える {% url ... %} というテンプレートタグでリンクが取得できます。

これはある画面(View関数)へアクセスするのに必要なリンクを返すテンプレートタグで、
たとえば / や /products/1/ のような値を返します。
引数には {% url 'product_list' %} のように、URLsに登録した名前を渡します。
ec/urls.py では商品一覧画面を name='product_list'、商品詳細画面を name='product_detail' と名付けています。
{% url 'product_list' %} とすると / が取得できます。

商品詳細画面へのリンクには商品のID値が必要です。
URLsでも 'products/<int:product_id>/' として変数を指定しています。
urlテンプレートタグからこの変数を渡すには、URLsで登録した名前に続けて値を渡します {% url 'product_detail' product.id %} 。
この product.id はテンプレートに渡された product オブジェクトの .id 値を渡しているという意味です。