DJangoのお勉強(1)

DJangoを使い始めたので覚書
Djangoの[チュートリアル](https://docs.djangoproject.com/ja/2.0/intro/tutorial01/)が何をやっているのかについての解説

今回使ったバージョン
pycharm:2017.3.3 (Community Edition)
Django Version: 	2.0.2
Python Version: 	3.6.3

# pycharmにプロジェクトを作る
個人的に、この環境で開発しているので、pycharmでDjangoを動かす為の準備を行う

1. pycharmを起動して、プロジェクトを作る
2. Djangoを使えるようにする
 1. (file)-(Setting) : [project:<プロジェクト名>]-[Project interpreter]
 2. 左上の[+]:Djangoを選択（似たような名前があるので、注意。最初が大文字）
 3. (install package)ボタンを押す
3. DJangoのプロジェクトを作る
 1. 下にある(terminal)タブを押す
 2. django-admin startproject <プロジェクト名>　　（testは名前がかぶるので使えない）
4. テストサーバーを起動してみる
 1. terminalから  
 2. cd <プロジェクト名>
 3. python manage.py runserver
 4. ブラウザでhttp://127.0.0.1:8000/にアクセスする

# アプリを作る
Djangoでは、プロジェクトの下に複数のアプリがあるという構成になる。
アプリは複数作っもいいし、ひとつでもいい。けど、とりあえずプロジェクトの下のアプリがあって初めてなんかできる状態になる。

アプリは、ターミナルより下記のコマンドで作成する
python manage.py startapp <アプリ名>

アプリを作成すると、プロジェクトルートの下に下記のディレクトリが作られる

```text:
<アプリ名>/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py
```

# URLディスパッチ
アプリを作っただけでは、アプリにアクセスする事は出来ない。
、、、ので、URLをディスパッチする為のルールを定義する

ディスパッチングのルールは、プロジェクトのurl.pyでアプリに振り分けるルールを定義し、アプリのurl.pyでアプリ内の振り分けを定義する。

[チュートリアル](https://docs.djangoproject.com/ja/2.0/intro/tutorial01/)では下記のように定義されている

```python:<プロジェクト名>/urls.py
from django.urls import include, path
from django.contrib import admin

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]
```
    path('polls/', include('polls.urls')),
は、urlが'polls/'だった場合は、polls/urls.pyを参照しなされという意味。

そのpolls/urls.pyは下記のように定義されている

```python:polls/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```
これは、polls/の下に何もない時は、view.pyのindex関数を呼び出すという意味になる
name=で、このURLパターンに名前をつけることが出来る。
ここでつけた名前によって実際のurlを取得する事が出来る

これを確かめるために、サンプルのview.pyを変更してテストしてみる

```python:polls/views.py
from django.http import HttpResponse
from django.urls import reverse

def index(request):
    urlName = reverse('index')
    return HttpResponse("Hello, world. You're at the polls index.{0}".format(urlName))
```
http://127.0.0.1:8000/polls/
にアクセスすると、下記のように表示される

>Hello, world. You're at the polls index./polls/

## path()

あらためて、urls.pyのpath()は、下記のインタフェースになっている。
参照[django.urls functions for use in URLconfs¶](https://docs.djangoproject.com/ja/2.0/ref/urls/#django.urls.path)

**path(route, view, kwargs=None, name=None)**

**route**は、urlパターンを含む、文字列、もしくは、gettext_lazy() 関数を指定する事が出来る。
gettext_lazy()については、多言語化に使うみたいだけど、今はよくわかっていない。
routeに指定する文字列には、<>でくくって、viewに渡す変数を指定する事が出来る。
すなわち、urlの一部がviewの引数として渡される。

これを確かめるために、polls/urls.pyとpolls/views.pyを下記のように変更してみる


```python:polls/urls.py
from django.urls import path

from . import views
import re

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:number>', views.subView, name='suburl'),
]
```

```python:polls/views.py
from django.http import HttpResponse
from django.urls import reverse

def index(request):
    urlName = reverse('index')
    return HttpResponse("Hello, world. You're at the polls index.{0}".format(urlName))

def subView(request, number):
    urlName = reverse('suburl', args=[number])
    return HttpResponse("これはsubViewです。{0} url={1}".format(number, urlName))
```

この状態で
http://127.0.0.1:8000/polls/1
にアクセスすると、
>これはsubViewです。1 url=/polls/1

と表示される

```
reverse('suburl', args=[number])と、args=[number]が指定されているのがミソ
<int:number>と変数を指定した場合は、reverseに対しても変数のタプルを渡してやらないとreverseが失敗する
```

pathのroute引数に指定された文字列のうち、<>で囲まれた<int:number>の部分において、intは変数の型を示し、numberは変数名を示す。変数の型には下記のものがある
[Path converters](https://docs.djangoproject.com/ja/2.0/topics/http/urls/#path-converters)を参照

|型指定|内容|
|:----------|:-----------------------------------------------|
|str|パスセパレータ '/'を除く、空でない文字列に一致します。 コンバータが式に含まれていない場合の既定値です。|
|int|ゼロまたは任意の正の整数に一致します。 intを返します。|
|slug|ASCII文字または数字、およびハイフンおよびアンダースコア文字で構成されるスラッグ文字列に一致します。 例えば、building-your-1st-django-siteのようにします。|
|uuid|フォーマットされたUUIDと一致します。 複数のURLが同じページにマッピングされないようにするには、ダッシュを含め、文字を小文字にする必要があります。 たとえば、075194d3-6885-417e-a8a8-6c931e272f00となります。 UUIDインスタンスを返します。|
|path|パスセパレータ '/'を含む、空でない文字列に一致します。 これにより、strのようなURLパスのセグメントではなく、完全なURLパスと照合することができます。|
(by google翻訳)

**view**は、urlがマッチした時に呼び出されるview関数を示す。
view引数は、ビュー関数またはクラスベースのビューのas_view（）の結果です。 また、django.urls.include（）でもかまいません。(by google翻訳)

ビュー関数は、このチュートリアルで使われているやり方で、Viewとして定義されている関数を指定する。
この関数の一番目の引数は、HttpRequest のオブジェクトで、第２引数以降は、もしあれば、routeの中で<>で指定された変数になります。

クラスベースのビューのas_view（）については、[Django でまず覚えたい TemplateView のパターン](https://qiita.com/ytyng/items/7cb3c3a5605974151678)に、記載されている説明がわかりやすかった。
テンプレートで使うようだけど、今の段階ではpendding。

django.urls.include（）は、他のURLconf moduleをここに展開するための指定です。
参照：[include()¶](https://docs.djangoproject.com/ja/2.0/ref/urls/#include)
詳しくは、今はやらないけど、<プロジェクト名>/urls.pyで既に使われている。

**kwargs**
この引数を指定すると、viewにここで指定した値が渡される。
この引数の型は辞書型でなければならない。
でも、今は試さない。

**name**
既に説明したとおり、このurlパターンに名前をつける。
viewの中ではreverse関数で、urlを参照出来るようになる。
とりあえず、今は、そんな程度の理解。
[URL パターンに名前をつける](https://docs.djangoproject.com/ja/2.0/topics/http/urls/#naming-url-patterns)に書かれているようだ。



## [url()](https://docs.djangoproject.com/ja/2.0/ref/urls/#url)
path()以外にもurlパターンを定義出来る。
この関数urlは、 django.urls.re_path().にエイリアスされていて、将来廃止される予定。

## [re_path()](https://docs.djangoproject.com/ja/2.0/ref/urls/#re-path)
そんなわけでre_path()

**re_path(route, view, kwargs=None, name=None)**
基本的にpath()と同じなのだけれども、route引数が、正規表現をとれるところが異なる。
route引数は、指定した文字列そのものが、正規表現として扱われるのでr""のようにregex文字列にする必要が無い一方で\を\でエスケープする必要もない。

たとえばpolls/test0,polls/test1,,,,polls/test9などの文字列に一致させる為のデモとして、polls/urls.pyとpolls/views.pyを以下のように書き換える


```python:polls/urls.py
from django.urls import path,re_path

from . import views
import re

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:number>', views.subView, name='suburl'),
    re_path('test[0-9]', views.repathView, name='re_path_url'),
]
```

```python:polls/views.py
from django.http import HttpResponse
from django.urls import reverse

def index(request):
    urlName = reverse('index')
    return HttpResponse("Hello, world. You're at the polls index.{0}".format(urlName))

def subView(request, number):
    urlName = reverse('suburl', args=[number])
    return HttpResponse("これはsubViewです。{0} url={1}".format(number, urlName))

def repathView(request):
    urlName = reverse('re_path_url')
    return HttpResponse("repathView。{0}".format( urlName))

```
この状態で、http://127.0.0.1:8000/polls/test1 にアクセスすると、下記のように表示される

> repathView。/polls/test0

この結果で分かる通り、urlのうち、特殊文字の部分は実際の値で反映されない。
この場合には、regex文字列で[シンボリックグループ名](https://docs.python.jp/3/library/re.html)を指定すると反映されるようになる

```python:polls/urls.py
from django.urls import path,re_path

from . import views
import re

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:number>', views.subView, name='suburl'),
    re_path('test(?P<number>[0-9])', views.repathView, name='re_path_url'),
]
```

```python:polls/views.py
from django.http import HttpResponse
from django.urls import reverse

def index(request):
    urlName = reverse('index')
    return HttpResponse("Hello, world. You're at the polls index.{0}".format(urlName))

def subView(request, number):
    urlName = reverse('suburl', args=[number])
    return HttpResponse("これはsubViewです。{0} url={1}".format(number, urlName))

def repathView(request, number):
    urlName = reverse('re_path_url', args=[number])
    return HttpResponse("repathView。{0} url={1}".format(number, urlName))
```
http://127.0.0.1:8000/polls/test3 とアクセスした結果は下記の通り

> repathView。3 url=/polls/test3


## [static()](https://docs.djangoproject.com/ja/2.0/ref/urls/#static)
デバッグモードでファイルを提供するためのURLパターンを返すヘルパー関数だそうです




詳細は、[django.urls functions for use in URLconfs](https://docs.djangoproject.com/ja/2.0/ref/urls/)と[URL ディスパッチャ]（https://docs.djangoproject.com/ja/2.0/topics/http/urls/#naming-url-patterns）に記載されているので参照のこと。