Django キャッチアップなど
Dockerfile
FROM python:3.12.5-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /django_udemy
COPY . /django_udemy/
RUN pip install --upgrade pip && \
pip install -r requirements.txt
compose
services:
app:
build: .
ports:
- '8000:8000'
volumes:
- .:/django_udemy
tty: true
stdin_open: true
command: /bin/bash
requirements.txt
Django==4.2.16
プロジェクトの下にアプリが積み重なっていく構成か。
以前に少しだけspring触ったが、全体的に感覚が違う。
とはいえ両方mvcフレームワークなので、この感覚の違いは記法に慣れないことが原因だろう。
簡単に手順殴り書き
-
django-admin startproject {名前} .
: プロジェクトの作成 -
python manage.py startapp {名前}
: アプリの作成 -
settings.py
で、"DIRS": [BASE_DIR / "templates"],
追記。 :TEMPLATES
のDIRS
を設定する。これで.html ファイルを参照するPATHが解決される。もちろんtemplates/も作っておこう。 -
settings.py
で、INSTALLED_APPS
に"{アプリ名}.apps.{アプリ名(頭文字は大文字に)}Config",
を追記。 : アプリを新規作成したので、記述が必要。 - うん!この粒度で書く必要なし!ここで取りやめ。
migrate
docker compose exec app python manage.py makemigrations {アプリ名}
docker compose exec app python manage.py migrate
管理画面にログインするスーパーユーザ作成
docker compose exec app python manage.py createsuperuser
管理画面に追加するために
- admin.pyに
admin.site.register(Model名)
モデルの文字列情報を定義したら管理画面の表示結果を制御できる。
-
def __str
を定義する。
CSRFとは
csrf_tokenの仕組み
TODOアプリまでDone.
あくまでハンズオンなのでここで油断するつもりはさらさらないが、思いのほかスムーズに進められた。
独学時にjava silver取って、その他の知識何もないままspring入門した時に感じた「少し基本を崩すと次に踏み出す足場がなくなる」ような感覚はなかった。
html/cssの基本を身につけたのがでかいのかなー。我ながらフロントの知識0からWEBフレームワーク進めようとしていたのはちょっと・・
admin.py
from django.contrib import admin
from .models import TodoModel
# Register your models here.
class TodoAdmin(admin.ModelAdmin):
list_display = ("title", "priority", "duedate")
search_fields = ("title",)
list_filter = ("priority", "duedate")
admin.site.register(TodoModel, TodoAdmin)
表形式で表示、検索窓、フィルター窓追加。
画像アップロード等の取り扱いを学んだ(開発環境限定)。このスクラップの本筋ではないが、こういうのはテンション管理も大事だと思うのでポスト。
enctype="multipart/form-data"
{% extends 'base.html' %}
{% block content %}
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
<p><input type="text" name="title" placeholder="title"></p>
<p><input type="text" name="content" placeholder="content"></p>
<p><input type="file" name="sns_image"></p>
<input type="hidden" name="author" value="{{ user.username }}">
<input type="submit" value="create">
</form>
{% endblock %}
- 通常のフォーム送信(application/x-www-form-urlencoded)では、テキストデータしか扱えない
- ファイルデータはバイナリデータなので、特別なエンコーディングが必要
- multipart/form-dataは、テキストとバイナリデータの両方を含むことができる
include()
include() の背景にある考えは、 URL を簡単にプラグ & プレイ可能にすることです。 polls には独自の URLconf (polls/urls.py) を持っているので、 "/polls/" 、 "/fun_polls/" や、 "/content/polls/" といった、どんなパスルート下にも置けて、どこに置いてもきちんと動作します。
project/urls.py
from django.urls import include, path
urlpatterns = [
# polls/urls.pyをインクルード
path('polls/', include('polls.urls')), # => /polls/...
path('fun_polls/', include('polls.urls')), # => /fun_polls/...
path('content/polls/', include('polls.urls')), # => /content/polls/...
]
# polls/urls.py
urlpatterns = [
path('', views.index, name='index'), # インデックスページ
path('<int:pk>/', views.detail, name='detail') # 詳細ページ
]
実現可能なURL
/polls/ -> インデックスページ
/polls/34/ -> ID 34の詳細ページ
/fun_polls/ -> 同じインデックスページ
/fun_polls/34/ -> 同じID 34の詳細ページ
/content/polls/ -> 同じインデックスページ
/content/polls/34/ -> 同じID 34の詳細ページ
Dockerの便利さを実感してきた。
環境構築に躓くことが格段に減る。シンプルな環境なら秒で用意(は言い過ぎか)できるのよき。
models.ForeignKey
question = models.ForeignKey(Question, on_delete=models.CASCADE)
どのようにして外部キーを結びつけている?
- コードだけではイマイチわからない・・
- 以下のような感じ?
Question テーブル
id | question_text | pub_date |
---|---|---|
1 | 好きな食べ物は? | 2024-11-12 10:00:00 |
Choice テーブル
id | choice_text | votes | question_id |
---|---|---|---|
1 | 寿司 | 0 | 1 |
2 | ラーメン | 0 | 1 |
まあ今のところこの理解で困っていないので、大丈夫そうだ。
Djangoが必要としているSQLが何であるかをスクリーンに表示
-
python manage.py sqlmigrate polls 0001
※よしなに読み替えて!
マイグレーションを作成したりデータベースにふれることなく、プロジェクトに何か問題がないか確認
python manage.py check
Question.objects.order_by("-pub_date")[:5]
from django.shortcuts import render
from django.http import HttpResponse
from .models import Question
# Create your views here.
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output)
- スライサーで取得できるってことはオブジェクトの配列って認識でおk?
- まあSQLのLIMIT句みたいなもんだよね。
遅延評価
# この時点ではまだSQLは実行されていない
questions = Question.objects.order_by("-pub_date")
# 以下のようなタイミングで実際にSQLが実行される
# 1. リストに変換時
list_questions = list(questions)
# 2. イテレーション時
for question in questions:
print(question)
# 3. スライス時
first_five = questions[:5]
- 挙動確認してみたいね。Django Debug Toolbarというものがあるらしい。キャッシュの動作確認とかもしてみたい。
テンプレートの名前空間
作ったテンプレートを (polls という別のサブディレクトリを作らずに) 直接 polls/templates の中に置いてもいいのではないか、と思うかもしれませんね。しかし、それは実際には悪い考えです。Django は、名前がマッチした最初のテンプレートを使用するので、もし 異なる アプリケーションの中に同じ名前のテンプレートがあった場合、Django はそれらを区別することができません。そのため、Django に正しいテンプレートを教えてあげる必要がありますが、一番簡単な方法は、それらに 名前空間を与える ことです。アプリケーションと同じ名前をつけた もう一つの ディレクトリの中にテンプレートを置いたのは、そういうわけなのです。
なるほど。
違和感
- 以下の書き方に違和感。
from django.shortcuts import render
from django.http import HttpResponse
from .models import Question
from django.template import loader
# Create your views here.
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))
- あっ、最初に学んだ教材はこういうふうにショートカットしてたのね。
from django.shortcuts import render
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)
-
これも
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})
-
こう
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
URL 名の名前空間
from django.urls import path
from . import views
+ app_name = "polls"
urlpatterns = [
path("", views.index, name="index"),
path("<int:question_id>/", views.detail, name="detail"),
path("<int:question_id>/results/", views.results, name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]
- <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
+ <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
静的ファイル
# プロジェクトのsettings.py
STATIC_URL = 'static/'
# 開発環境での静的ファイル設定
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# 本番環境用(必要な場合)
STATIC_ROOT = BASE_DIR / 'staticfiles'
Django のソースファイルの場所確認
$ python -c "import django; print(django.__path__)"
リモートリポジトリからcloneしてきた.mdを自動表示する簡易ブログ作成してみる
- 理解度チェック && 興味があったので着手してみた
Hoge.objects.all()
- もし結果が0でも例外発生しない。空のリストが戻ってくる
トランザクションのデフォルト設定をトランザクションの有効範囲をビューの開始から終了までにする
DATABASES = {
'default': {
..
'ATOMIC_REQUESTS': True, # 追加
}
}
SecurityMiddleware
- 「HTTP」でアクセスした場合に 「HTTPS」 で始まる URL に自動的にリダイレクトするようにレ スポンスを返す(デフォではfalse)
SECURE_SSL_REDIRECT = True