<style> /* basic design */ .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p { font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic'; text-align: left; line-height: 1.6; letter-spacing: normal; text-shadow: none; word-wrap: break-word; color: #444; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {font-weight: bold;} .reveal h1, .reveal h2, .reveal h3 {color: #2980b9;} .reveal th {background: #DDD;} .reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;} .reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;} .reveal table {margin: 1.0vw auto;} .reveal code {line-height: 1.2;} .reveal p, .reveal li {padding: 0vw; margin: 0vw;} .reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #EEE; border-radius: 1.5vw;} /* table design */ .reveal table {background: #f5f5f5;} .reveal th {background: #444; color: #fff;} .reveal td {position: relative; transition: all 300ms;} .reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #aaa;} .reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #fff;} /* blockquote design */ .reveal blockquote { width: 90%; padding: 0.5vw 0 0.5vw 6.0vw; font-style: italic; background: #f5f5f5; } .reveal blockquote:before{ position: absolute; top: 0.1vw; left: 1vw; content: "\f10d"; font-family: FontAwesome; color: #2980b9; font-size: 1.0vw; } /* font size */ .reveal h1 {font-size: 5.0vw;} .reveal h2 {font-size: 2.0vw;} .reveal h3 {font-size: 1.5vw;} .reveal h4 {font-size: 1.2vw;} .reveal h5 {font-size: 1.0vw;} .reveal h6 {font-size: 0.8vw;} .reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 1.2vw;} .reveal code {font-size: 1.2vw;} /* new color */ .red {color: #EE6557;} .blue {color: #16A6B6;} /* split slide */ #right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;} #left {left: 31.25%; text-align: left; float: left; width: 50%; z-index: -10;} </style> <style> /* specific design */ .reveal h2 { padding: 0 1.5vw; margin: 0.0vw 0 2.0vw -2.0vw; border-left: solid 1.2vw #2980b9; border-bottom: solid 0.8vw #d7d7d7; } </style> <!-- --------------------------------------------------------------------------------------- --> ## GtugGirls ハンズオン <br> # Django ペアプロ ###### tags: `Django` `GTUGGirls` `DRF` `presentation` <br> <br> 2020/09/06(日) --- ## 自分用メモ - [ ] 資料ざっくり作成 - [ ] 見直し - [ ] 日本語訂正 - [ ] requirements.txt でインストールするもの最終決定 - [x] VScodeのFileTreeのフォントを大きくする = window.zoomLevel を大きくする.全体が大きく表示される [VSCode のエクスプローラー(ファイルツリー)のフォントサイズを変える - Qiita](https://qiita.com/ktrkmk/items/451147b6da6735513fac) - [x] Vscode family font を Ricty Diminished に変更 - [ ] python のインストールで,macos の場合最初から入っている2形は使うなという指示 - [ ] やんざむさんに https://sqlitebrowser.org/ をいれといて,というお願い - [ ] やんざむさんの環境をさらっと教えてもらう - [ ] CSS,Template はサラッと流す --- ## 事前準備 python 3.* 以上 python 2 系が入っている人は、 python をインストール後 (https://www.python.org/downloads/) terminal で呼び出す時に `python3` で呼び出す必要があるかもしれない。 --- ## ディレクトリ作成 ```python $ mkdir gtug_django $ cd gtug_django ``` --- ## 仮想環境を作成 ### 現在のpython を確認 ```bash $ which python /your/path/bin/python ``` ### 仮想環境作成 ```bash # gtug_djangoディレクトリの中で $ python -m venv .venv ``` + `python -m venv <仮想環境名>`  + `.venv` は慣習的によく使われる仮想環境名 ### 確認 ```bash $ ls -la .venv ``` ### 仮想環境に入る ```bash $ source .venv/bin/activate ``` ### python 確認 ```bash $ which python /gtug_test/.venv/bin/python ``` --- ## Djangoインストール 1 仮想環境にpipを使って、Djangoや他のライブラリをインストール ### pip + パッケージ管理システム + pip は .venv を作った時に一緒にインストール済み。 + `which pip` で確認できる + pip を使ったライブラリのinstall 方法 1. `pip install <ライブラリ名>` 2. `pip install -r <ライブラリリストを記入したファイル>` ←今回はこっち --- ## Djangoインストール 2 ### インストール 1. `requirements.txt` 作成 ```bash $ touch requirements.txt $ vi requirements.txt ``` 2. installしたいライブラリを追記.現在 Django3系が最新ですが,まだサードパーティのライブラリが追いついていないなど色々アレなので,2系の最新を入れます ```python Django==2.2.14 djangorestframework django-rest-auth django-allauth ``` 3. install ```bash $ pip install -r requirements.txt ``` 4. 確認 ```bash $ pip freeze ``` --- ## Djangoプロジェクト作成 ### 一般的なプロジェクト作成方法 ```bash $ django-admin startproject <プロジェクト名> ``` + <プロジェクト名> というディレクトリを作成して、その下にプロジェクトに必要なファイルを自動生成 + このコマンドの問題点(参照:[現場で使える Django の教科書《基礎編》](https://www.amazon.co.jp/dp/4802094744) P21 ) + このコマンドでプロジェクトを作ると、設定用のディレクトリがベースディレクトリ名(`<プロジェクト名>`)で作成されてわかりづらい + テンプレートや静的ファイルがアプリケーション毎にバラバラに配置される雛形になる ### 今回のプロジェクト作成方法 ```bash # gtug_django ディレクトリの中で $ django-admin startproject config . ``` + 第一引数に設定用ディレクトリ名、第二引数に 「.」 + `gtug_django` をプロジェクトディレクトリにして、設定ファイルは config ディレクトリ配下に集めるという方法 + これで、プロジェクト名は `gtug_django` 、設定ディレクトリは `config` になりわかりやすい。 + テンプレートや静的ファイルの設定は後ほど + 昨今,日本のDjango界で勢力を伸ばしている akiyokoさんのオススメ方法 --- ## 今のプロジェクトTree ```bash $ tree . . ├── config │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── requirements.txt ``` ### manage.py プロジェクト管理コマンドユーティリティ * データベースやテーブルの作成変更 * 新しいアプリケーション作成 * サーバーのスタートなど --- ## きょうつくるアプリの説明 + あとでかく + (Django Girls Tutorialの blog サイトいんすぱいあー) + (一部あきよこさん形式に変えて作成する) + urls + http://127.0.0.1:8000/blog : 投稿した記事一覧 + http://127.0.0.1:8000/post : 記事を書く画面 + http://127.0.0.1:8000/1 : 1記事を表示 + http://127.0.0.1:8000/delete/1 : 1記事を削除 + http://127.0.0.1:8000/update/1 : 1記事を更新 + http://127.0.0.1:8000/create/ : 新規作成 --- ## Djangoアプリケーション + 一般的にアプリケーションというと,それだけで全ての機能を持つ完結したソフトウェア + Djangoでは、1プロジェクトの配下にある、1単体機能のこと + たとえば,ユーザーアカウントを管理する機能, + ユーザーの日記を管理する機能... + アプリケーションは、ディレクトリ毎に作成されるので、 + 他のプロジェクトにそのディレクトリを「コピペ」(に近い形)することで転用可 + 一度作れば他のプロジェクトにも使える、「ライブラリ」みたいな使い方も --- ## Djangoアプリケーション ### 作成方法 1. 新規アプリケーション作成 ```bash $ python manage.py startapp <アプリケーション名> ``` 2. `settings/config.py` の `INSTALLED_APPS` にアプリ追加 --- ## Djangoアプリケーション ### Custom User アプリケーション + まず第一に作ったほうが絶対幸せ + Djangoには超絶強力なツール「**管理ツール**」がある. + これを使うには superuser が必要 + superuser を作るために user アプリが必要 + Djangoは デフォルトの User アプリケーションがあるので,カスタムを作らなくても supuruser は作成可 + **HOWEVER,** + この デフォルト User アプリケーション が<font color=red>諸悪の根源</font>で,今後開発を進めていくうえで不幸しかない + オフィシャルなドキュメントにも, > プロジェクトの開始時にカスタムのユーザーモデルを使用する¶ 新しくプロジェクトを始める場合は、デフォルトの User で十分である場合でも、カスタムユーザーモデルを作成することを強く推奨します。このモデルはデフォルトのユーザーモデルと同様に動作しますが、必要に応じて将来的にカスタマイズすることができます: https://docs.djangoproject.com/ja/2.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project + と記載がある. --- ## Djangoアプリケーション ### Custom User アプリケーション #### 1. `python manage.py startapp <アプリケーション名>` ```bash $ python manage.py startapp user ``` ```bash $ tree . -L 2 . ├── config │ ├── __init__.py │ ├── __pycache__ │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py ├── requirements.txt └── user ├── __init__.py ├── admin.py ├── apps.py ├── migrations ├── models.py ├── tests.py └── views.py ``` --- ## Djangoアプリケーション ### Custom User アプリケーション #### 2. settings/config.py の `INSTALLED_APPS` にアプリ追加 ```bash └── user ├── __init__.py ├── admin.py ├── apps.py # ←このファイルの中の class 名を追記 ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py ``` `settings/config.py` ```python INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'user.apps.UserConfig', # ←追記 ] ``` --- ### Custom User アプリケーション #### Custom Userを使うために必要な設定 Defaultのユーザーではなく、今作った user アプリを使うようにDjangoに教える設定。細かい説明はあとで行うのでとりあえずコピペで進んで下さい。 ##### user/models.py ```python from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): pass ``` ##### user/admins.py ```python from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import CustomUser class CustomUserAdmin(UserAdmin): model = CustomUser admin.site.register(CustomUser, CustomUserAdmin) ``` --- ### Custom User アプリケーション #### Custom Userを使うために必要な設定 ##### config/settings.py ```python # プロジェクトにデフォルトUserではなく,Custom User を使うように設定 AUTH_USER_MODEL = 'user.CustomUser' SITE_ID = 1 ``` ##### ターミナルで ```bash $ python manage.py makemigrations user $ python manage.py migrate ``` --- ## database 確認 はじめて `python manage.py migrate` すると,プロジェクトディレクトリ直下に `db.sqlite3` が作成される ```bash $ tree . -L 1 . ├── config ├── db.sqlite3 # ←これ ├── manage.py ├── requirements.txt └── user ``` [DB Browser for SQLite](https://sqlitebrowser.org/)を使ってサクッと確認 ![](https://i.imgur.com/iLp20bv.jpg) --- ## Superuser 作成 + ProjectのUseruserを作成 + 管理ツールへのログインが出来るなど,なんでも出来るユーザー ```bash $ python manage.py creatuperuser Username: admin # なんでもいい Email address: # エンターでパス出来る Password: # パスワードを記入しても表示されない.心配しなくて大丈夫 Password (again): # 同じもの⇑を書く Superuser created successfully. # これが表示されたらOK ``` --- ## Superuser 作成 + SQLite で superuser が作成されたか確認 ![](https://i.imgur.com/73ENrAQ.jpg) --- ## Run server サーバーを立ち上げる ```bash $ python manage.py runserver ``` + http://localhost:8000/ + ロケットは発射できた=はじめての runserver できた --- ## ホットリロード体験 + サーバーを立ち上げたまま + `config/settings.py` で表示環境を英語→日本語に変更 ```python #LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'ja' ``` + ファイル保存→ブラウザリロード. --- ## 管理ツール + Djangoの超絶便利機能 + http://127.0.0.1:8000/admin/ + superuser でログイン + [新刊『現場で使える Django 管理サイトのつくり方』頒布のお知らせ - akiyoko blog](https://akiyoko.hatenablog.jp/entry/2020/08/12/092908) --- ## 管理ツール ### 管理ツールをつかってデータを追加 ![](https://i.imgur.com/EAVAs73.jpg) --- ## Djangoアプリケーション ### blog アプリケーション ブログアプリケーションを作成 1. `python manage.py startapp <アプリケーション名>` ```bash $ python manage.py startapp blog ``` 2. `settings/config.py` の `INSTALLED_APPS` にアプリ追加 ```python INSTALLED_APPS = [ : : 'user.apps.UserConfig', 'blog.apps.BlogConfig', # 追記 ] ``` --- ## Model ### データ管理はすべて models.py DBテーブル管理は全て,`<アプリ名>/models.py` で行う. + テーブル作成 + データの定義を変更 + など... ### 今回必要なデータ + タイトル + 記事 + 作成日時 + 書いた人 --- ## Model ### blog model `blog/models.py` ```python from django.db import models from django.utils import timezone from user.models import CustomUser class Post(models.Model): # ブログタイトル title = models.CharField(max_length=50, blank=False, null=False) # 記事 text = models.TextField(blank=False, null=False) # 作成日時 created_at = models.DateTimeField(default=timezone.now) # 筆者 author = models.ForeignKey(CustomUser, on_delete=models.CASCADE) ``` + [django.db.models](https://docs.djangoproject.com/ja/2.2/topics/db/models/) に用意されている「フィールド」とよばれるオブジェクトを使いデータを定義 + データの型や、オプション(文字数や、Default値など)も設定可 + これを使って定義することでデータ一つ一つを python のオブジェクトとして扱う事ができる + ForeignKeyで、他のテーブルを参照 + `on_delete=models.CASCADE`: 削除するオブジェクトに紐づいたオブジェクトを全て削除 --- ## migration ##### Django での migration とは? `models.py` の変更を実際のDBのに反映させることを **migration** という ##### migration コマンド 以下2つ ```bash $ python manage.py makemigrations <アプリ名> # 変更があった場合その変更の準備をする $ python manage.py migrate # 変更を適用する ``` `blog/models.py` を書き換えたからので migrate しましょう ```bash $ python manage.py makemigrations blog $ python manage.py migrate ``` --- ## DB 確認 (いちいちしなくてもいいけど。) ![](https://i.imgur.com/AwBfyMM.jpg) --- ## admin `<アプリ名>/admin.py` は、作成したモデルを管理画面に登録する時に使うファイルです。 登録すると、管理ツールからサクッとデータを追加できます。 ### 登録方法 1. 管理画面 http://127.0.0.1:8000/admin/ 立ち上がっているか確認 2. blog/admin.py ```python from django.contrib import admin from .models import Post # 同じ階層の models.py から Post をインポート admin.site.register(Post) # 管理ツールにPostを登録 ``` 3. おもむろに,管理画面ブラウザをリロード ![](https://i.imgur.com/zsvGp8a.jpg) --- ## Dataをいれてみよう ### post を追加 Posts の `+ 追加`を押す ![](https://i.imgur.com/TEVrOs2.jpg) + `blog/models.py` で定義した,あの定義に従ってフォームを自動生成 + Author は、ForeinKeyでUserテーブルを結んだので参照できるようになっている + 作成日時は default=timezone.now に従って現在時刻が挿入される + **ノート: あなたの環境はサーバー時間より、9時間進んでいます。** と薄く書いてありますね。これは嫌なので日本時間に変更します。 --- ## Dataをいれてみよう ### post を追加(ローカルタイムに変更) `config/setting.py` ```python TIME_ZONE = 'Asia/Tokyo' #'UTC' ``` 保存したら、ブラウザをリロードして下さい。 続けて、適当にブログを2,3本入れて下さい。 --- ## Dataをいれてみよう ### postリストをタイトルに変更したい ポストを作成すると、Post Object として python で扱える形になります。 Post のリスティングされているところでPost Object (1), Post object (2)... と表示されているのはそれです。 ![](https://i.imgur.com/TdQzRYY.jpg) これではわかりにくいので、Postタイトルを表示するように変更します。 --- ## Dataをいれてみよう ### postリストをタイトルに変更したい `blog/models.py` ```python class Post(models.Model): title = models.CharField(max_length=200) .... # 追記 def __str__(self): return self.title ``` **memo**: 今回は,`models.py` に変更を加えたけど管理画面の表示だけの話なので migrate は必要なし. --- ## View ### ViewがDjangoの要め ![](https://i.imgur.com/bjTIj5b.png) 1. HttpRequest (HttpRequest オブジェクト)を受け取って 1. 必要な演算を行い 1. レスポンス情報を作成 ### view を挟んだ流れ 1. WSGIRequestクラス(django.core.handlers.wsgi)が HttpRequestを取得してリクエストオブジェクトを作成 2. リクエストオブジェクトを urls.py に書かれたマッピングに従ってView関数にわたす 4. View関数が、 4. リクエストオブジェクトに含まれた入力したデータを取得して型変換 5. データのValidation 6. Validation OK であれば、ビジネスロジック処理。レスポンスの為の変数を用意 7. 遷移先画面を決定 8. 遷移先画面用に変数を使って内容をレンダリング 9. レスポンスオブジェクトの内部にレンダリングされたHTMLコンテンツを埋めてURLに投げる --- ## View Viewのかんたんな例:([公式Documentより](https://docs.djangoproject.com/ja/2.2/topics/http/views/#a-simple-view)) `blog/vies.py` ```python from django.http import HttpResponse import datetime def current_datetime(request): # リクエストを受取り, hello = "Hello Django" html = "<html><body>Say, %s!</body></html>" % hello return HttpResponse(html) # レスポンスを返す ``` `settings/urls.py` ```python from django.contrib import admin from django.urls import path from blog.views import current_datetime # 追加 urlpatterns = [ path('admin/', admin.site.urls), path('test/', current_datetime), # 追加 ] ``` `test/`が呼ばれたら `current_datetime` を実行するように指定 --- ## View ### 2つの書き方 1. 関数型 : `def` で作る 2. **クラスベース型** : `class` で作る ### 圧倒的にクラスベース型が主流 + model へのインターフェイス + template 処理 + リクエスト,レスポンス処理 などたくさんのクラスが用意されている  [Built-in class-based views API](https://docs.djangoproject.com/en/2.2/ref/class-based-views/) --- ## View ### クラスベース型使い方 ```python # 使いたいViewをインポート from django.views.generic import TemplateView # 自分のViewクラスに継承して class AboutView(TemplateView): # カスタマイズしたいところだけする template_name = "about.html" ``` 複数のViewを継承することも可 --- ## View ### 注意: クラスベース型Viewを使う時は「継承の順番」に気をつける ```python class ViewA(TemplateView, LoginRequiredMixin): .... ``` これでは動作せず ```python class ViewB(LoginRequiredMixin, TemplateView): .... ``` これなら動作する --- ## View ### [View](https://docs.djangoproject.com/ja/2.2/ref/class-based-views/base/#view) 全てのクラスベースViewの元になる view . `blog/views.py` ```python from django.http import HttpResponse from django.views import View class MyView(View): def get(self, request, *args, **kwargs): return HttpResponse('Hello, World!') ``` --- ## View `config/settings.py` ```python from django.contrib import admin from django.urls import path from blog.views import MyView # 追記 urlpatterns = [ path('admin/', admin.site.urls), # path('test/', current_datetime), path('test/', MyView.as_view()), # 追記 ] ``` `.as_view()`して関数化する http://127.0.0.1:8000/test/ にアクセスすると表示が確認できる --- ## View ### 投稿した記事一覧 http://127.0.0.1:8000/blog/ にアクセスしたら,投稿したblogがリストされたウェブページの表示をするための view を作成 リストするためには 1. Modelを通じて,DBに今の全Blog postの問合せをする 2. それを template に rendering する --- ## View ### 投稿した記事一覧 ### [List Views](https://docs.djangoproject.com/ja/3.1/ref/class-based-views/generic-display/#listview) ```python from django.views.generic.list import ListView from .models import Post class PostListView(ListView): model = Post # リスティングしたいモデルを指定 ``` `settings/urls.py` ```python from blog.views import MyView, PostListView #追記 urlpatterns = [ path('admin/', admin.site.urls), path('test/', MyView.as_view()), path('blog/', PostListView.as_view()), #追記 ] ``` http://127.0.0.1:8000/blog/ **TemplateDoesNotExist at /blog/** というエラーが出れば正解 --- ## View ### 投稿した記事一覧 エラー画面 <font color="red">注目:Exception Value</font> **`blog/post_list.html` が無いと言っている** ![](https://i.imgur.com/3S9rxBl.jpg) --- ## View ### 投稿した記事一覧 ```python class PostListView(ListView): model = Post # リスティングしたいモデルを指定 ``` このコードだけで + blog/post_list.html というテンプレートをレンダリングする + `post_list` という変数名に Post モデルの全レコードを渡す + `post_list` は、ListView が、`モデル名_list` という形で勝手に作ってくれるオブジェクト という動作をする。 <font color="red"> つまり、`blog/post_list.html`というテンプレートがあると期待しているので、**TemplateDoesNotExist at /blog/** というエラーを返す</font> --- ## View ### 投稿した記事一覧 #### テンプレートの置き場所を設定 `config/settings.py` ```python TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', #'DIRS': [], 'DIRS': [os.path.join(BASE_DIR, 'templates')], #追記 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] ``` + `DIRS` にディレクトリを指定しておくと、 View がそのディレクトリ配下のテンプレートを優先的に探します。 + `'APP_DIRS': True,` は、テンプレートを探す時に各アプリケーションディレクトリ直下の templatesディレクトリ を最も優先して探します。 + 上記の例では、まず `<アプリ名>/templates`を探し、次に `gtug_django/templates`配下を探す --- ## View ### 投稿した記事一覧 #### テンプレート作成 ```bash $ mkdir -p blog/templates/blog/ $ touch blog/templates/blog/post_list.html ``` ここで一旦、http://127.0.0.1:8000/blog/ をリロードしてみましょう。 さっきのエラー画面は消えたはずです。 --- ## View ### 投稿した記事一覧 #### テンプレート作成 Djangoには、HTMLの中に Django の python でつくった変数をつかってデータを表示したり、条件分岐などのロジックを書いたりすることができます。 これは Django Template Language (DTL)と呼ばれるエンジンです。 #### 変数にはいったデータにアクセス `{{ 変数名 }}` でアクセス可 `blog/templates/blog/post_list.html` ```html {{ post_list }} ``` http://127.0.0.1:8000/blog/ をリロード ``` <QuerySet [<Post: タイトル1>, <Post: タイトル1>]> ``` --- ## View ### 投稿した記事一覧 #### テンプレート作成 色々ためしましょう ```html {{ post_list }} <br> {{ post_list.0 }} <br> {{ post_list.0.text }} <!-- models.py で使った変数名でアクセス可 --> ``` --- ## View ### 投稿した記事一覧 #### テンプレート作成 for loop ```html {% for post in post_list %} {{ post.title}}<br> {% endfor %} ``` という感じです。ちょっとかっこよくしましょう ```html <html> <head> </head> <body> <div> <h1><a href="/">GTUGGirls Django blog</a></h1> </div> {% for post in post_list %} <div> <p>published: {{ post.created_at }}</p> <h2><a href="">{{ post.title }}</a></h2> <p>{{ post.text|linebreaksbr }}</p> </div> {% endfor %} </body> ``` --- ## View ### 投稿した記事一覧 #### テンプレート作成 extends 別のテンプレートを取り込んで必要な部分だけ上書き出来る。 --- ## View ### 投稿した記事一覧 #### css DjangoGirls さんからデザインはお借りしました :bow: [CSSでカワイくしよう · HonKit](https://tutorial.djangogirls.org/ja/css/#css%E3%81%A8%E3%81%AF%EF%BC%9F) ```html <head> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"> <link href="//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext" rel="stylesheet" type="text/css"> <link rel="stylesheet" href="{% static 'css/blog.css' %}"> </head> ``` blog.css ```css .page-header { background-color: #C25100; margin-top: 0; padding: 20px 20px 20px 40px; } .page-header h1, .page-header h1 a, .page-header h1 a:visited, .page-header h1 a:active { color: #ffffff; font-size: 36pt; text-decoration: none; } .content { margin-left: 40px; } h1, h2, h3, h4 { font-family: 'Lobster', cursive; } .date { color: #828282; } .save { float: right; } .post-form textarea, .post-form input { width: 100%; } .top-menu, .top-menu:hover, .top-menu:visited { color: #ffffff; float: right; font-size: 26pt; margin-right: 20px; } .post { margin-bottom: 70px; } .post h2 a, .post h2 a:visited { color: #000000; } ``` --- ## View ### 投稿した記事一覧 #### css の置き場所 [静的ファイル (画像、JavaScript、CSS など) を管理する | Django ドキュメント | Django](https://docs.djangoproject.com/ja/2.2/howto/static-files/) + アプリケーション内に static というフォルダを作って静的ファイルを保存 + ディレクトリの説明を書く `config.settings.py` ```python # PROJECT_NAME = os.path.basename(BASE_DIR) STATIC_URL = '/static/' #STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] #STATIC_ROOT = os.path.join(BASE_DIR, 'static_root') #STATIC_ROOT = '/var/www/{}/static'.format(PROJECT_NAME) #MEDIA_URL = '/media/' #MEDIA_ROOT = os.path.join(BASE_DIR, 'media_root') #MEDIA_ROOT = '/var/www/{}/media'.format(PROJECT_NAME) ``` --- ## View ### 投稿した記事一覧 #### post_list.html ```htmlmixed= {% load static %} <html> <head> <title>GTUG Girls blog</title> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"> <link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'> <link rel="stylesheet" href="{% static 'css/blog.css' %}"> </head> <body> <div class="page-header"> <h1><a href="/">GTUG Girls Blog</a></h1> </div> <div class="content container"> <div class="row"> <div class="col-md-8"> {% for post in post_list %} <div class="post"> <div class="date"> {{ post.created_at }} </div> <h2><a href="">{{ post.title }}</a></h2> <p>{{ post.text|linebreaksbr }}</p> </div> {% endfor %} </div> </div> </div> </body> </html> ``` --- ## template 拡張 共通部分だけ base.html というファイル記述し、そのファイルを継承し、上書きして体裁を整える ``` $ touch blog/templates/blog/base.html ``` + post_html.html をコピーして base.html にペースト + base.html このブロックを ```html {% for post in post_list %} <div class="post"> <div class="date"> {{ post.created_at }} </div> <h2><a href="">{{ post.title }}</a></h2> <p>{{ post.text|linebreaksbr }}</p> </div> {% endfor %} ``` この2行に置き換える ```html {% block content %} {% endblock %} ``` --- ## template 拡張 `post_html.html` ```html {% extends 'blog/base.html' %} {% block content %} {% for post in post_list %} <div class="post"> <div class="date"> {{ post.published_date }} </div> <h2><a href="">{{ post.title }}</a></h2> <p>{{ post.text|linebreaksbr }}</p> </div> {% endfor %} {% endblock %} ``` --- ## 個別リンク ```htmlmixed= <h2><a href="">{{ post.title }}</a></h2> ``` ここに個別リンクを貼りたい `blog/views.py` ```python from django.views.generic.detail import DetailView class PostDetailView(DetailView): model = Post ``` `settings/urls.py` ```python from blog.views import PostListView, PostDetailView path('blog/<int:pk>', PostDetailView.as_view(), ), ``` http://127.0.0.1:8000/blog/1 > TemplateDoesNotExist at /blog/1 blog/post_detail.html --- `blog/post_detail.html` ```html {% extends 'blog/base.html' %} {% block content %} <h2><a href="">{{ post.title }}</a></h2> <p>{{ post.text|linebreaksbr }}</p> {% endblock %} ``` --- ## フォーム + 記事を追加したり編集する為にフォームを作ります + 入力データのValidation + デフォルトではフォーム用の python ファイルは用意されていないので新規作成 ``` $ touch blog/forms.py ``` `blog/forms.py` 入力するのは、タイトルと本文だけにして、created_at は作成した時間を自動挿入、author はその時ログインしていたひと、ということにする。 ```python from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: # ここで使うモデルやフィールドを伝える model = Post fields = ('title', 'text',) # この2つしかいらないよ、と伝える ``` --- ## フォーム ### Validation 入力値のバリデーションを行いたい時、クラスメソッド関数を定義する + clean_<フィールド名> : フィールド単位 + clean : 複数のフィールド間の相関チェックやDBとの整合性 --- ## フォーム ### Validation #### clean_<フィールド名> + 入力値は、`self.cleaned_data`という辞書型データにはいっている + validation OK であれば、その値を return する。そうすると、Djangoが内部的に `self.cleaned_data["title"]` に再セットしてくれる + もしこれを忘れると`self.cleaned_data`から値が削除されてしまうので注意 ```python class PostForm(forms.ModelForm): class Meta: # ここで使うモデルやフィールドを伝える model = Post fields = ('title', 'text',) # この2つしかいらないよ、と伝える def clean_title(self): # titleは3文字以上でなくてはいけないというルールにしたとする title = self.cleaned_data["title"] if len(title) <= 3: raise forms.ValidationError( "title くらい3文字以上書けよ" ) return title # 忘れずにreturn ``` --- ## フォーム ### Validation #### clean + return しなくてもOK + title と text に NG word が入っていないか確認 ```python class PostForm(forms.ModelForm): .... def clean(self): title = self.cleaned_data["title"] text = self.cleaned_data["text"] ngword = "進捗" try: title.find(ngword) > 0 or text.find(ngword) > 0 except: raise forms.ValidationError("進捗については聞かないでください。パワハラです。") ``` --- ## フォーム ### view + [CreateView](https://docs.djangoproject.com/en/2.2/ref/class-based-views/generic-editing/#django.views.generic.edit.CreateView)を継承して view を作る ```python from django.views.generic import ListView, CreateView from .forms import PostForm class PostCreateView(CreateView): model = Post form_class = PostForm ``` --- ## フォーム ### urls conf ```python from blog.views import PostListView, PostCreateView urlpatterns = [ path('admin/', admin.site.urls), path('blog/', PostListView.as_view()), path('blog/new/', PostCreateView.as_view(), ), # 追記 ] ``` --- ## フォーム ### urls conf localhost:8000/blog/new/ ![](https://i.imgur.com/bnICrPi.jpg) `blog/post_form.html `がないよ、と言ってる。 よって作成してあげる ``` $ touch blog/templates/blog/post_form.html ``` リロードするととりあえずエラーは消えます --- ## フォーム ### テンプレート `blog/templates/blog/post_form.html` ``` {{ form }} ``` ダサいけどフォームでてきた。 ![](https://i.imgur.com/ERSyGiN.jpg) --- ## フォーム ### テンプレート + save ボタン追加 + `{{ form.as_p }}` で pタグの中にいれたhtmlに出来る `blog/templates/blog/post_form.html` ``` <form method="post">{% csrf_token %} {{ form.as_p }} <button type="submit" class="save btn btn-default">Save</button> </form> ``` --- ## フォーム ### テンプレート + base.html を継承 ```html {% extends 'blog/base.html' %} {% block content %} <form method="post">{% csrf_token %} {{ form.as_p }} <button type="submit" class="save btn btn-default">Save</button> </form> {% endblock %} ``` --- ## URL configuration ### 基本  ルーティングは `config/urls.py` に書く ```bash . ├── blog ├── config │ ├── settings.py │ ├── urls.py # ルーティングはここに書く │ └── wsgi.py ├── db.sqlite3 ├── manage.py ├── requirements.txt └── user ``` --- ## URL configuration ### 基本  `config/urls.py` の `urlpatterns`にルーティングを書く ```python from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ] ``` 初期設定されているので http://127.0.0.1:8000/admin/ で管理画面に入れる pathの文法: ```python path('パス/', 'url定義'), ``` >`admin.site.urls` がキニナル良い子のみなさん `.venv/lib/python3.6/site-packages/django/contrib/admin/sites.py` のなかの `def urls(self):`をさがして定義をおってみてください. `def get_urls(self)` という定義のなかに `urlpatterns` があります. --- ## URL configuration ### 基本+α でも,アプリケーションが増えてくるとここだけに書くのは非常に読みづらくなるので, 各アプリケーションの配下に `urls.py` を手動で作成し, その `urls.py`を `config/urls.py` に include する形を取ったほうが今後幸せです. つまり, 1. `touch blog/urls.py` 2. `blog/urls.py`にルーティングを書く 3. `config/urls.py`にインクルードする ```python from django.urls import path, include : : urlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls')), ] ``` --- ## URL configuration ### 基本+α ```bash $ touch blog/urls.py ``` ---
{"metaMigratedAt":"2023-06-15T11:05:42.608Z","metaMigratedFrom":"YAML","title":"GtugGirls Django","breaks":true,"slideOptions":"{\"theme\":\"white\",\"slideNumber\":\"c/t\",\"center\":false,\"transition\":\"none\",\"keyboard\":true,\"width\":\"93%\",\"height\":\"100%\"}","contributors":"[{\"id\":\"00a2e83d-c22f-488a-a657-78a8fd18a059\",\"add\":40072,\"del\":11586}]"}
    1333 views