Django 開発者への道② ~ URLを設計する ~

さて、Djangoの技術記事連載企画「Django 開発者への道」
第二弾がやってきました！

**前回の記事**
[Django 開発者への道① ~ Modelsを理解する ~]
(https://qiita.com/kotayanagi/items/3cfadae951c407ac044a)


## はじめに

前回記事の第一弾では、
DjangoのModelsとDBの設計方法について学習しました。

今回の第二弾では、
URLの設計とルーティングの実装方法について学習していきます。

まずは、フォルダ構成を再確認しながら、
Djangoのプロジェクトに存在する2つの `urls.py` ファイルを見ていきます。

2つ同じ名前のファイルが存在する意味を、
しっかり理解した上で、
**ルーティングの実装方法**や、、
**ベストプラクティス**などをみていきます。

最終的に、ルーティングを設計し、
**動的にルーティングを設計できる**ようになるのがゴールです！

**元記事**
[Django: URLの設計について]
(https://just-python.com/article/article-django-urls)

## ルーティングって何ですか？

まず、ルーティングとは何でしょうか？

ルーティングとは、

**送られてきたデータを、次のページ（ルート）へ転送する仕組みのことです。**

プロダクトを開発する上で、
たくさんのページが作られるのは、当然のこと。

### 例: hogehoge.com


hogehoge.com というドメインを取得して、ブログを作るとすると、

- ダッシュボード
- 記事一覧ページ
- 記事の詳細ページ

など、自ずとプロダクトには、複数以上のページができます。

その時、当然ですが、
上記3つ全てに同じ hogehoge.com というURLを与えることはできません。
URLは、一つのページの在り処みたいなものなので、
それぞれのページにそれぞれのURLを設定する必要があります。

- **ドメイン（hogehoge.com）以降のURLをパスと呼びます。**

ダッシュボードに、hogehoge.com というURLパスを使用し、
一覧ページでは、 hogehoge.com/articles
詳細ページでは、 hogehoge.com/articles/1

など、それぞれに違うURLパスを与える必要があります。

それらのURLパスを
ページごとに効率よく与えられる機能が
Djangoには備わっているのです。


## Djangoのプロジェクト構成


### 親ディレクトリと子ディレクトリ


- **親ディレクトリ**

仮想環境などを整え、以下のコマンドを打つ。

```
django-admin startproject blog
```

すると、**blog** というプロジェクトが立ち上がりますね。

自動的に、以下のファイルとフォルダが作成されます。

- manage.py
- blog (ディレクトリ)
    - settings.py
    - urls.py
    - その他省略


この時、プロジェクトを立ち上げた段階で、
自動作成される blog ディレクトリを **親ディレクトリ** と呼びましょう。


- **子ディレクトリ**

さて、`manage.py` ファイルがある階層で、以下のコマンドを打つ。

```
python manage.py startapp article
```

すると、**article** というapplicationが作成され、
プロジェクト構成が以下のようになります。


- manage.py
- blog
- article
    - views.py
    - models.py
    - admin.py
    - その他ファイル


この `python manage.py startapp` によって作成されるディレクトリを
**子ディレクトリ**と呼びましょう！


- **注意点**

`python manage.py startapp` で作成されるディレクトリの中には、
`urls.py`が存在しません。

よって、自分でファイルを作成する必要があります。
ファイル作成後は、以下の構成になります。

- manage.py
- blog
- article
    - views.py
    - models.py
    - admin.py
    - **urls.py**
    - その他ファイル


## 親urls と 子urls

では、親urlsと子urlsの違いはなんでしょうか？

Djangoは、中・大規模でのプロダクト作成を前提としているため、
urlパスが非常に多くなることを想定としています。

例えば、プロジェクト内のアプリケーション数が10くらい作られた場合、
ページ数が、100を超えるなんてこともあると思います。

その時、100を超えるパスを一つのファイルに書くと、運用しづらくなります。

また、アプリケーションごとに、ディレクトリが分けられるため、
`urls.py`ファイルも一緒に分けてしまい、子urlsを親urlsに引き継がせてしまおうという考えだと思っています。


- **流れ**

1. `blog/urls.py`の親urlsでは、それぞれのアプリケーション（`article`）の子urlsを読み込む。

2. 子urlsで、アプリケーション内のURLパスを記述する。


## ルーティングの実装

では、実際に、ルーティングの実装をしていきましょう。
例として、

- ブログTOPページ
- お問い合わせページ
- 記事一覧ページ
- 記事詳細ページ

をそれぞれ、実装していきたいと思います。


### 親urls

まずは、親urlsから実装していきます。


```blog/urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import include
from django.conf import settings
from django.conf.urls.static import static

from article import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name="index"),
    path('', include('article.urls')),
] + static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

```

親urlsで書く必要があるパスは主に三つです。

- 管理サイトパス（admin）
- ページダッシュボードのパス（index）
- アプリケーションのパス
- *その他、もし外部ライブラリなどを使用した時も、実装が必要な場合があります。


まず、必要なメソッドや関数、viewsなどをimportしておきます。

- + 以降の static などは、今回は扱いません。
CSSやJS、写真などのファイルを読み込めるようにする設定だと思っておけば大丈夫です。

さて、`urlpatterns` に代入してあるリストの中身が、URLパスになっています。


```.py
path('admin/', admin.site.urls),
```
上記は、管理サイトのURLパスになっています。
`hogehoge.com/admin` で管理サイトに入れるようになります。
ここまでは、元々プロジェクト作成時点で、実装されています。

```.py
path('', views.index, name="index"),
```
上記のURLパスは、articleから読み込んだviewsから、indexを読み込んでいます。
ページのダッシュボードのために実装が必要になります。

```.py
path('', include('article.urls')),
```
上記が、アプリケーションのパスを読み込んでいるものになります。

`include`は英語で、~を含む という意味です。
つまり、articleのurlsを含ませるという意味になります。
非常に直感的ですね :) 


- **おさらい**

path() で、URLパスを記載します。

`path(A,B,*C)`

上記において、

- Aには、パスが入ります。
    - '/hoge' など
- Bには、viewsが入ります。
    - views.pyから、関数をimportしたものを記載します。
- Cには、nameが入ります。
    - nameを指定することで、パスを指定しやすくなり、またプロジェクト全体の可読性が高くなります。
    - nameは任意です。




それでは、次に、アプリケーション（ここではarticle）のURLパスを実装していきましょう！

### 子urls

```article/urls.py
from django.urls import path
from . import views

app_name= 'article'
urlpatterns = [
    path('', views.index, name='index'),
    path('article/list', views.list, name='list'),
]

```

親urlsと同じように、`django.urls`から path を import します。
そして、viewsをimport します。

- **app_name**

アプリケーションに属する子urlsでは、app_nameを指定することで、
アプリケーションに明示的に、名前を指定することができます。

こうすることで、プロジェクト内に複数のアプリケーションを作成した時に、
それぞれのアプリケーションを分かりやすく区分けすることができます。

こちらも、必須ではありませんが、指定しておくと、
プロジェクト全体の可読性があがるので、覚えておくことをお勧めします。

さて、パスを実装していきます。

- ブログTOPページ (index)
- 記事一覧ページ (list)

```.py
path('', views.index, name='index'),
path('article/list', views.list, name='list'),
```

同じように、まず、パスを指定します。

- '', 'article/list'

そして、viewsの関数を引用します。

- index, list

最後に、nameを指定します。

- 'index', 'list'

ルーティングの実装はこれでOKです！

viewsについては、本シリーズの第三弾 Django 開発者への道③で学習していくので、今は、気にしなくて大丈夫です。


## 動的なルーティングの実装

それでは、最後に、少し難易度は上がりますが、動的なルーティングの実装を見ていきます。

```article/urls.py
from django.urls import path
from . import views

app_name= 'article'
urlpatterns = [
    path('', views.index, name='index'),
    path('article/list', views.list, name='list'),
    path('article/<int:pk>', views.detail, name='detail'), # ここを追加
]

```

追加した部分、

```.py
path('article/<int:pk>', views.detail, name='detail'), # ここを追加
```
を見てみましょう。

何やら、暗号みたいなパスが出てきました。
今は、<>は暗号と覚えておきましょう。笑

<>の中に、型と名前を指定することで、
動的なトークンが作成します。

つまり、article/ 以降のパスを動的に生成することができるようになります。

**<>の中は 型 : 名前（なんでも良い）になります。**

<>で指定できる主要な型

- int （数値）
- str (文字列)
- slug (URLに使われる半角英数字と記号)

また、ここで名前の部分に、pkと記載していますが、

```.py
path('article/<int:pk>', views.detail, name='detail'), # ここを追加
```
primary key の略で、よくデータのIDを示す時に使用されます。

つまり、`article/1` や `article/2` などが入るようになり、

models.py で作成したDBテーブルの IDが入るんだと、今はイメージできればOKです。

あとは、viewsの detail を読み込み、　nameに 'detail'を指定することで、
動的なURLの完成です。

詳しくは第五弾で学習することになりますが、ここで指定したパスはテンプレート（html)で指定することができます。
下記はaタグを使った一例です。

```.html
<a href="{% url 'article:list' %}">
```

また、URLに動的な値を追加したい場合、

```.html
<a href="{% url 'article:detail' object.id %}">
```

と記述することができます。

`{% url 'A:B' *C %}`

- Aは namespaceで指定した値を入れます
- Bは、nameで指定した値
- *Cは、動的なパスの場合に、データに基づいた値を入れたりします。
    - ここでは、IDを代入しています。


namespace や name を明示的に書くことで、プロジェクトの可読性が上がるという意味がわかったのではないでしょうか？

- 一目みただけで、どのアプリケーションに実装してあるのか、
- どのパスをさしてるのか

などが分かりやすくなります。


## 終わりに

今回は、urlsにて、プロジェクトのルーティングを実装する方法をみていきました。

今回は第二弾ということで、

- 第一弾のModels
- 第二弾のUrls

をもう一度おさらいします。


### Models (DB)

**以下の三つのDBテーブルを作成**

- カテゴリ(Category)
- 記事（Article）
- コメント（Comment）

```article/models.py
from django.db import models


class Category(models.Model):
    
    name = models.CharField(max_length=31, verbose_name="カテゴリ名")

    def __str__(self):
        return self.name

    class Meta:

        verbose_name_plural = "カテゴリ"


class Article(models.Model):

    title = models.CharField(max_length=62, verbose_name="タイトル")
    slug = models.SlugField(verbose_name="URLスラッグ（英語）")
    image = models.ImageField(upload_to="article_image", blank=True, verbose_name="記事のイメージ写真", help_text="登録しない場合は、デフォルトのイメージ写真を使用する")
    categories = models.ManyToManyField(Category, verbose_name="カテゴリ")
    body = models.TextField(verbose_name="本文")
    status = models.PositiveSmallIntegerField(default=1, verbose_name="公開ステータス", help_text="1:下書き, 2:公開")
    liked = models.IntegerField(default=0, verbose_name="いいね数")
    is_deleted = models.BooleanField(default=False, verbose_name="削除フラグ")
    creator = models.CharField(max_length=31, verbose_name="投稿者"）
    created = models.DateTimeField(auto_now_add=True, verbose_name="投稿日時")
    modified = models.DateTimeField(auto_now=True, verbose_name="更新日")

    def __str__(self):
        return self.title

    class Meta:
        
        verbose_name_plural = "記事"
    


class Comment(models.Model):

    article = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="記事", related_name="comments")
    commenter = models.CharField(max_length=31, verbose_name="コメント者名")
    body = models.TextField(verbose_name="コメント文")
    created = models.DateTimeField(auto_now_add=True, verbose_name="コメント投稿日時")

    def __str__(self):
        return self.article.title + ":{}のコメント".format(self.commenter)

    class Meta:

        verbose_name_plural = "コメント"
```


### URLs (ルーティング)

**ダッシュボードと記事を閲覧するためのページへのルーティング**

- index (hogehoge.com) ダッシュボード
- list (hogehoge.com/article/list) 記事一覧ページ
- detail (hogehoge.com/article/`<int:pk>`) 記事詳細ページ

```article/urls.py
from django.urls import path
from . import views

app_name= 'article'
urlpatterns = [
    path('', views.index, name='index'),
    path('article/list', views.list, name='list'),
    path('article/<int:pk>', views.detail, name='detail'), # ここを追加
]

```


### まとめ

以下、今日学んだことを簡潔にまとめます。

- URLの実装箇所は、親urlsと子urlsがある
- 子urlsは、アプリケーションディレクトリに、自分で作成する必要がある
- 作成したアプリケーションの数だけ、子urlsを作成する
- 親urlsでは、子urlsを読み込む（includeする）
- URLパスを作成するには、path()と、パス、views関数、nameを引数として指定する
- データに基づいた動的なURLを作成するには、<>を使用する
- 主に使う型は、int(数値),str(文字列),slug(URLスラッグ)
- `<int:pk>` や `<str:title>` や `<slug:article_slug>` などのように使用する

以上になります。

長くなりましたが、

**Django 開発者への道② ~ URLを設計する ~**
を終了します。

次回は、
**Django 開発者への道③ ~ Viewsを設計する① ~**
です。

乞うご期待！