CORS (cross origin resource sharing)

  • オリジン間リソース共有Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組み
  • XMLHttpRequestや Fetch API は同一オリジンポリシーsame-origin policyに従う
  • XMLHttpRequestや Fetch API 使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンの場合は正しい CORS ヘッダーを含んでいることが必要
  • オリジン間共有仕様は、以下のようなサイト間 HTTP リクエストを有効にする
    • XMLHttpRequest または Fetch API を呼び出す
    • CSS の @font-face で別ドメインのフォントを利用
    • WebGL テクスチャ
    • drawImage() (en-US) を使用してキャンバスに描画される画像やビデオフレーム
    • 画像から生成する CSS シェイプ
  • オリジン間リソース共有の仕様は、ウェブブラウザーから情報を読み取ることを許可されているオリジンをサーバーが記述することができる、新たな HTTP ヘッダーを追加することで作用します。
  • サーバーの情報に副作用を引き起こすことがある HTTP のリクエストメソッド (特に GET 以外の HTTP メソッドや、特定の MIME タイプを伴う POST) のために、ブラウザーが HTTP の OPTIONS リクエストメソッドを用いて、あらかじめリクエストの「プリフライト」 (サーバーから対応するメソッドの一覧を収集すること) を行い、サーバーの「認可」のもとに実際のリクエストを送信することを指示
  • サーバーはリクエスト時に「資格情報」 (Cookie や HTTP 認証 など) を送信するべきかをクライアントに伝えることもできる
  • CORS は様々なエラーで失敗することがありますが、セキュリティ上の理由から、エラーについて JavaScript から知ることができないよう定められている
  • 何が悪かったのかを具体的に知ることができる唯一の方法は、ブラウザーのコンソールで詳細を見ること
  • request type
    • Simple requests
    • Preflighted requests
    • Requests with credentials

単純リクエス

  • リクエストによっては CORS プリフライトを引き起こさないものがあり、これを「単純リクエスト」と呼ぶ
  • 「単純リクエスト」は、以下のすべての条件を満たすものです。
    • 許可されているメソッドのうちの一つであること。
      • GET
      • HEAD
      • POST
    • ユーザーエージェントによって自動的に設定されたヘッダー (たとえば Connection、 User-Agent、 または Fetch 仕様書で「禁止ヘッダー名」として定義されているヘッダー) を除いて、手動で設定できるヘッダーは、 Fetch 仕様書で「CORS セーフリストリクエストヘッダー」として定義されている以下のヘッダーだけ
      • Accept
      • Accept-Language
      • Content-Language
      • Content-Type (但し、下記の要件を満たすもの)
      • DPR
      • Downlink (en-US)
      • Save-Data
      • Viewport-Width
      • Width
    • Content-Type ヘッダーでは以下の値のみが許可されています。
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • リクエストに使用されるどの XMLHttpRequestUpload にもイベントリスナーが登録されていないこと。これらは正しく XMLHttpRequest.upload を使用してアクセスされます。
    • リクエストに ReadableStream オブジェクトが使用されていないこと。
  • 注: これらはウェブコンテンツが発行可能になっているサイト間リクエストと同じ種類のものであり、サーバーが適切なヘッダーを送信しなければレスポンスデータは送信元へ送られません。従ってクロスサイトリクエストフォージェリ対策をしているサイトは、 HTTP アクセス制限について新たに心配することはありません。
  • 注: WebKit Nightly および Safari Technology Preview は、 Accept, Accept-Language, Content-Language ヘッダーの値に追加の制限を掛けています。これらのヘッダーが「標準外」の値の場合、 WebKit/Safari はそのリクエストが「単純リクエスト」の条件に合うとは判断しません。 WebKit/Safari がこれらのヘッダーのどの値を「標準外」と判断するかについては、以下の WebKit のバグを除いて文書化されていません。Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language / Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS / Switch to a blacklist model for restricted Accept headers in simple CORS requests / これは仕様の一部ではないので、他のブラウザーはこの追加の制限を実装していません。

// src
const xhr = new XMLHttpRequest();
const url = 'https://bar.other/resources/public-data/';

xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();
bar.otherへの リクエスト 
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

特筆すべきリクエストヘッダーは Origin であり、呼び出しが https://foo.example から来たことを表す

bar.otherからのレスポンス
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

レスポンスでは、サーバーが Access-Control-Allow-Origin ヘッダーを返信

この場合、サーバーは Access-Control-Allow-Origin: * を返しており、これはそのリソースがすべてのドメインからアクセスできることを意味

https://bar.other にあるリソースの所有者が、リソースへの制限を https://foo.example からのリクエストのみに制限したい場合

Access-Control-Allow-Origin: https://foo.example

Origin ヘッダーと Access-Control-Allow-Origin ヘッダーの使用は、最も単純なアクセス制御プロトコルを表す

プリフライトリクエス

  • 始めに OPTIONS メソッドによる HTTP リクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうかを確かめる
  • サイト間リクエストがユーザーデータに影響を与える可能性があるような場合に、このようにプリフライトを行う
// exsample
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');

POST で送信する XML の本体を作成
標準外の X-PINGOTHER HTTP リクエストヘッダーを設定
このようなヘッダーは HTTP/1.1 プロトコルに含まれていませんが、ウェブアプリケーションでは一般的に便利
Content-Type に application/xml を使用し、かつカスタムヘッダーを設定しているため、このリクエストではプリフライトを行う
注: 後述するように、実際の POST リクエストには Access-Control-Request-* ヘッダーが含まれず、 OPTIONS リクエストのみで必要になります。

// プリフライトリクエスト
OPTIONS /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

// プリフライトレスポンス
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive

・リクエスト解説
ブラウザーは上記で使用された JavaScript コードで使用しているリクエストの引数に基づいて、プリフライトの送信が必要であることを判断
これによりサーバーは実際のリクエストの引数によって、送られるリクエストが受け入れ可能かをレスポンスできる
OPTIONS はサーバーから付加的な情報を得るために用いる HTTP/1.1 のメソッドであり、また安全なメソッド、つまりリソースを変更するためには使用できないメソッド
Access-Control-Request-Method ヘッダーは、プリフライトリクエストの一部として、実際のリクエストが POST リクエストメソッドで送られることをサーバーに通知
Request-Headers ヘッダーは、実際のリクエストにカスタムヘッダーである X-PINGOTHER および Content-Type が含まれることをサーバーに通知
ここでサーバーは、この状況下でリクエストの受け入れるかを判断する機会がある

・レスポンス解説
Access-Control-Allow-Methods は当該リソースへの問い合わせに POST および GET が実行可能であることを伝える
Access-Control-Allow-Methods ヘッダーはレスポンスヘッダーの Allow と似ていますが、アクセス制御でのみ使用
サーバーは、 Access-Control-Allow-Headers を X-PINGOTHER の値で送信し、これが実際のリクエストで使用されるヘッダーであることを承認
Access-Control-Allow-Headers は受け入れ可能なヘッダーをカンマ区切りのリストで表す
Access-Control-Max-Age は、プリフライトリクエストを再び送らなくてもいいように、プリフライトのレスポンスをキャッシュしてよい時間を秒数で与える
この例では86400秒、つまり24時間
ブラウザーは個々に内部の上限値を持っており、 Access-Control-Max-Age が上回った場合に制限を掛ける

// 本命のリクエスト
POST /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache

<person><name>Arun</name></person>


// 本命のレスポンス
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some XML payload]
プリフライトリクエストとリダイレクト
  • 多くのブラウザーは現在、下記のようなプリフライトリクエストのリダイレクトに未対応
  • プリフライトリクエストにリダイレクトが発生すると、多くのブラウザーは以下のようなエラーメッセージを報告

"The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight"
"Request requires preflight, which is disallowed to follow cross-origin redirect"

  • もともと CORS プロトコルはそのような振る舞いを要求してたが、その後で必要なしに変更されたが、多くのブラウザーはまだ変更を実装しておらず、もともと要求されていた振る舞いに従っている
  • ブラウザーが仕様に追いつくまで、以下の一方もしくは両方を行うことでこの制限を回避できる
    • サーバー側の振る舞いを変更して、プリフライトが発生しないようにするか、リダイレクトが発生しないようにする
    • リクエストをプリフライトを起こさない単純リクエストなどに変更する
  • これらの変更ができない場合は、次のような別な方法があります。
    • 単純リクエストを行い (Fetch API の Response.url (en-US) または XMLHttpRequest.responseURL を使用して)、実際のプリフライトリクエストが転送される先を特定する。
    • 最初のステップの Response.url または XMLHttpRequest.responseURL で得た URL を使用して、もう一つのリクエスト (「本当の」リクエスト) を行う。
  • リクエストに Authorization ヘッダーが存在するためにプリフライトを引き起こすリクエストの場合、上記の手順を使用して制限を回避できない

資格情報を含むリクエス

  • XMLHttpRequest や Fetch と CORS の両方が、 HTTP クッキーと HTTP 資格情報によってわかる「資格情報を含む」リクエストを作成することができる
  • 既定では、サイト間の XMLHttpRequest または Fetch の呼び出しにおいて、ブラウザーは資格情報を送信しない
  • XMLHttpRequest オブジェクトまたは Request のコンストラクターの呼び出し時に、特定のフラグの設定が必要
  • http://foo.example から読み込まれた元のコンテンツが、 http://bar.other にあるリソースに対してクッキーを設定したシンプルな GET リクエス
const invocation = new XMLHttpRequest();
const url = 'http://bar.other/resources/credentialed-content/';

function callOtherDomain() {
  if (invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}

XMLHttpRequest に設定する必要があるフラグ、 withCredentials という真偽値型の値を設定
既定では、クッキーなしで呼び出す
単純な GET リクエストなのでプリフライトは行いませんが、ブラウザーAccess-Control-Allow-Credentials: true ヘッダーを持たないレスポンスを拒否し、ウェブコンテンツを呼び出すレスポンスを作成しない

// request
GET /resources/credentialed-content/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2


// response
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


[text/plain payload]

http://bar.other 向けのクッキーが含まれていますが、bar.other が Access-Control-Allow-Credentials: true (17行目) をレスポンスに含めなければ、レスポンスは無視されウェブコンテンツで使用できない

資格情報付きリクエストとワイルドカード
  • 格情報を含むリクエストに対するレスポンスの時、サーバーは Access-Control-Allow-Origin ヘッダーで "*" ワイルドカードではなくオリジンを指定しなければならない
  • 上記のリクエストヘッダーは Cookie ヘッダーを含んでいるため、 Access-Control-Allow-Origin ヘッダーが "*" であったらリクエストは失敗
  • Access-Control-Allow-Origin ヘッダーの値が "*" ワイルドカードではなく "http://foo.example" (実際のオリジン) なので、ウェブコンテンツの呼び出しに対して資格情報を意識したコンテンツが返る
  • 上記の中にある Set-Cookie レスポンスヘッダーは、将来のクッキーの設定も行ないます。失敗した場合、 (使われている API によりますが) 例外が発生
  • CORS のレスポンスに設定されたクッキーは、サードパーティーのクッキーに関する通常のポリシーに従う
  • 上記では、ページは foo.example から読み込まれてるが、クッキーは bar.other から送られているので、ユーザーがブラウザーサードパーティーのクッキーをすべて拒否するよう設定していた場合は保存されない

HTTP レスポンスヘッダー

  • Access-Control-Allow-Origin
    • Access-Control-Allow-Origin は、リソースへのアクセスを許可するオリジンをブラウザーに伝えるための単一のオリジンを指定することができます。
    • 資格情報を含まないリクエストのみ、どのオリジンにもリソースへのアクセスを許可することをブラウザーに伝えるワイルドカード "*" を指定することができます。
    • サーバーがワイルドカード "*" ではなく (ホワイトリストの一部としてリクエストするオリジンに基づいて動的に変更される可能性がある) 単一のオリジンを指定した場合は、サーバーは Vary レスポンスヘッダーに Origin も含めて、サーバーのレスポンスが Origin リクエストヘッダーの値によって変化することをクライアントに知らせる必要がある

Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin

  • Access-Control-Max-Age
    • プリフライトリクエストの結果をキャッシュしてよい時間
    • 秒単位
  • Access-Control-Allow-Credentials
    • credentials フラグが真であるときに、リクエストへのレスポンスを開示してよいか否かを示す
    • プリフライトリクエストのレスポンスの一部として用いられたときは、実際のリクエストで資格情報を使用してよいか否かを示す
    • 単純な GET リクエストはプリフライトを行いませんので、リソースへのリクエストが資格情報付きで行われた場合にリソースと共にこのヘッダーを返さなければ、レスポンスはブラウザーによって無視され、ウェブコンテンツに返らない
  • Access-Control-Allow-Methods
    • リソースへのアクセス時に許可するメソッドを指定
    • プリフライトリクエストのレスポンスで用いる
  • Access-Control-Allow-Headers
    • 実際のリクエストでどの HTTP ヘッダーを使用できるかを示すために、プリフライトリクエストのレスポンスで使用

HTTP リクエストヘッダー

  • サイト間 XMLHttpRequest の機能を使用する開発者は、オリジン間リソース共有のヘッダーをプログラムで設定する必要なし
  • Origin
    • サイト間のアクセスリクエストやプリフライトリクエストのオリジンを示します。
    • パス情報は含めず、サーバー名だけにします。
    • すべてのアクセス制御リクエストにおいて、 Origin ヘッダーは常に送信
  • Access-Control-Request-Method
    • プリフライトリクエストを発信する際に、実際のリクエストを行う際に使用する HTTP メソッドをサーバーに知らせるために使用
  • Access-Control-Request-Headers
    • プリフライトリクエストを発信する際に、実際のリクエストを行う際に使用する HTTP ヘッダーをサーバーに知らせるために使用

django思い出しメモ

Django

環境立ち上げ

python3 -m venv env
source env/bin/activate

プロジェクト作成

django-admin startproject プロジェクト名

アプリ作成

python manage.py startapp アプリ名

サーバの起動

python manage.py runserver

リクエストハンドラーの作成

from django import HttpResponse

def index(request):
    return HttpResponse("Hello")
  • ビューをurlと紐付ける

1. app/urls.pyを作成する
2.app/urls.pyに追加する

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

3.project/urls.pyに追加する

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]

path関数の説明

1.path() 引数: route----------------------------------------------------

route は URL パターンを含む文字列です。リクエストを処理するとき、Django は urlpatterns のはじめのパターンから開始し、リストを順に下に見ていきます。要求された URL を一致するものを見つけるまで各パターンと比較します。

パターンはGETやPOSTのパラメーター、そしてドメイン名を検索しません。例えば、 https://www.example.com/myapp/ へのリクエストにおいては、URLconfは myapp/ を見ます。 https://www.example.com/myapp/?page=3 へのリクエストにおいても、URLconfは myapp/ を見ます。


2.path() 引数: view----------------------------------------------------

Django がマッチする正規表現を見つけると、 Django は指定されたビュー関数を呼び出します。その際は HttpRequest オブジェクトを第一引数に、そしてキーワード引数としてrouteから「キャプチャされた」値を呼び出します。この例はこの後すぐ出てきます。


3.path() 引数: kwargs----------------------------------------------------

任意のキーワード引数を辞書として対象のビューに渡せます。この機能はチュートリアルでは使いません。


4path() 引数: name----------------------------------------------------

URL に名前付けをしておけば Django のどこからでも明確に参照でき、とくにテンプレートの中で有効です。この便利な機能のおかげで、プロジェクトのURLにグローバルな変更を加える場合にも1つのファイルを変更するだけで済むようになります。

データベースバインディング·コンフィグレーション

1. 関連リンクDjango のインストール方法 | Django ドキュメント | Django
2. DATABASES の 'default' 項目内の以下のキーをデータベースの接続設定に合うように変更してください。

ENGINE -- 'django.db.backends.sqlite3'、 'django.db.backends.postgresql'、 'django.db.backends.mysql' または 'django.db.backends.oracle' のいずれかにします。その他のバックエンド も利用可能です。
NAME -- データベースの:setting:NAME です。SQLiteを使用している場合、データベースはコンピュータ上のファイルになります。デフォルト値の BASE_DIR / 'db.sqlite3' は、プロジェクトディレクトリにファイルを保存します。

USER や PASSWORD そして HOST などの追加設定を加える必要があります

3.sqlite以外の場合
もし SQLite 以外を使っている場合、 database を今のうちに作っておいてください。 "CREATE DATABASE database_name;" とデータベースのインタラクティブプロンプトで実行してください。

mysite/settings.py のデータベースユーザに 「データベース作成」の権限があるならばDB を自動作成することができます。

詳細リンク設定 | Django ドキュメント | Django

4.project/settings.py を編集する際、 TIME_ZONE に自分のタイムゾーンも設定します。

基本DBの作成

djangoインスタンスを起動するには最低1つのDBを取り扱うのでその準備をしなければなりません。

python manage.py migrate

これで準備完了です。

モデルの作成

from django.db import models

class MyModel(models.Model):
     data1 = models.CharField(max_length=100)

アプリ用DBの作成

1.Djangoインスタンス(project/settings.py)にアプリを登録する

INSTALLED_APPS = [
    ~~~~
    'polls.apps.PollsConfig',
]

2. マイグレーションの作成
python manage.py makemigrations アプリ名
3.マイグレイトの実行
python manage.py migrate

可読性をあげる1(strを使えるようにする)

モデルに__str__メソッドを追加する。

def __str__(self):
        return self.member

urlパターン マッチングをする

path('<int:some_integer>/detail/', views.my_view, "description")

templateの使用

1.app/templates/app/を作成
2.上記ディレクトリ にhtmlファイルを作成
3.viewで使用する
loaderを使用する場合

from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

renderを使用する場合

from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

404エラーハンドリング

ショートカットなし

from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

ショートカット有り

from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

テンプレートシステムの基本

1.for文

{% for a in b %}
~~~
{% endfor %}

2.if文

{%  if contidtion %}
~~~
{% else %}
~~~
{% endif %}

3.式

{{ a.attribute }}

urlのハードコーディングを避ける。

基本イディオム

{% url 'urls.pyのnameに対応する文字列' %}

もし他のアプリケーションでも同じnameのビューがあった場合の対処法
app/urls.pyでapp_nameの定義をし、url関数の引数を変更する。

app_name = 'myapp'
{% url 'myapp:detail' %}

csrf_tokenの使用

<form action="~~~" method="post">
{% csrf_token %}
<input ~~~>
<input type="submit" >
</form>

POSTデータの取り出し

request.POST['key']

リダイレクト

HttpResponseRedirectを使用する引数にはurlを渡す。

urlの作成

reverse関数を使用する

reverse('app:name', args=(パラメータ))

汎用ビュー(リストビュー)の使用

class ItemsView(generic.ListView):
    template_name = 'app/items.html'
    context_object_name = 'latest_item_list'

    def get_queryset(self):
        return MyModel.objects.order_by('-pub_date')[:5]

get_querysetをオーバーライドすることでビューに渡すデータを変更できる
template_nameで使うテンプレートを指定できる

汎用ビュー(デテールビュー)の使用

詳細ページを作成するときに使う

class ItemView(generic.DetailView):
    model = MyModel
    template_name = "app/item.html"

決まり
templateではmodel名を小文字にした物が変数として使われる。
詳細ビューのデータ取得にはurlのpkを使っているので
urls.pyの文字列で/itemのようにしないといけない
template_nameで使うテンプレートを指定できる

testを書く

書く場所はapp/tests.py

#基本形
import datetime

from django.test import TestCase
from django.utils import timezone

from .models import Question


class QuestionModelTests(TestCase):

    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() returns False for questions whose pub_date
        is in the future.
        """
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)

1.TestCaseを継承したクラスを作成する。
2.

python manage.py test app

を実行することでtest_で始まるメソッドが端から実行される

ビューのテストを書く

# 基本形
class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
        """
        If no questions exist, an appropriate message is displayed.
        """
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

静的要素の使用

1.app/static/app/を作成
2.上記ディレクトリ ないに静的ファイルを設置
3.テンプレートで読みこむ

{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">

Django Adminの使用

管理ユーザの作成

python manage.py createsuperuser

admin機能の使用

1./admin/にアクセスします
2.ログインします

アプリをadminに登録する。

app/admin.pyに下記のコードを追加する

from .models import Question

admin.site.register(Question)

フォームの変更

モデルの admin のオプションを変更したいときには、モデルごとに admin クラスを作成して、 admin.site.register() の 2 番目の引数に渡すと いうパターンに従ってください。
ex. app/admin.py

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question_text']

admin.site.register(Question, QuestionAdmin)

この例ではフォームの並び順を変更しています。

リレーションオブジェクトの追加

class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3

class QuestionAdmin(admin.ModelAdmin):
   ~~~
    inlines = [ChoiceInline]

オブジェクト一覧ページの変更

class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date', 'was_published_recently')

フィルタリング 検索

class Question(models.Model):
    # ...
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'
list_filter = ['pub_date']
search_fields = ['question_text']

adminテンプレートのカスタム

1.設定ファイル (mysite/settings.py) を開いて、TEMPLATES 設定オプションの中に、次のように DIRS オプションを追加します。

    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [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',
            ],
        },
    },
]

2.manage.pyと同じ階層にtemplates/admin/を作成しましょう
3.django/contrib/admin/templates/admin/base_site.htmlを2のディレクトリ にコピーします。
4.お好きなように編集してください。

django.contrib.admin.AdminSiteを変更すれば一部の値は簡単に変更できる。

day 48 調べたこと。

---調べたこと--- ---結果---
vscodeでのパネル間移動 cmd + shift + E
vscodeでのターミナル の開き方 ctl + shift + `
新規ファイルの作成 cmd + N
新規ファイル作成のカスタム keybindings.jsonを編集する。*1
はてなでの注釈 コンテンツ((注釈))
vscodeでのコミット Ctrl+Shift+G
vscodeでのサイドバーの開閉 cmd + B
画面左側の虫眼鏡などがあるパネルの名前 アクティビティバー
行削除 cmd + shift + K
chromeでタブを閉じる command + w

考察

引数にする。
レシーバにする。
関数にする。

・taro.greet();
・greet(taro);
・greet_taro();

どれがいいのか。

基本
引数は少ない方がいい。
レシーバも同様。
とすると基本的には3番目がいいということになる?
3番目は表面的依存もないし、抽象度も高いし3番目をラッパーとして1を使ったりするのがいい?