Closed41

Django キャッチアップなど

R2I5wR2I5w

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
R2I5wR2I5w

python manage.py runserverでlocalhost8000が動作してない。いや、データが読み込めませんに変わったので通信自体は何かしらできてる?

R2I5wR2I5w

プロジェクトの下にアプリが積み重なっていく構成か。
以前に少しだけspring触ったが、全体的に感覚が違う。
とはいえ両方mvcフレームワークなので、この感覚の違いは記法に慣れないことが原因だろう。

R2I5wR2I5w

簡単に手順殴り書き

  1. django-admin startproject {名前} . : プロジェクトの作成
  2. python manage.py startapp {名前} : アプリの作成
  3. settings.pyで、"DIRS": [BASE_DIR / "templates"],追記。 : TEMPLATESDIRSを設定する。これで.html ファイルを参照するPATHが解決される。もちろんtemplates/も作っておこう。
  4. settings.pyで、INSTALLED_APPS"{アプリ名}.apps.{アプリ名(頭文字は大文字に)}Config",を追記。 : アプリを新規作成したので、記述が必要。
  5. うん!この粒度で書く必要なし!ここで取りやめ。
R2I5wR2I5w

migrate

  • docker compose exec app python manage.py makemigrations {アプリ名}
  • docker compose exec app python manage.py migrate
R2I5wR2I5w

管理画面にログインするスーパーユーザ作成

  • docker compose exec app python manage.py createsuperuser
R2I5wR2I5w

モデルの文字列情報を定義したら管理画面の表示結果を制御できる。

  • def __strを定義する。
R2I5wR2I5w

TODOアプリまでDone.

https://www.udemy.com/course/django-3app/learn/lecture/23384130

あくまでハンズオンなのでここで油断するつもりはさらさらないが、思いのほかスムーズに進められた。

独学時にjava silver取って、その他の知識何もないままspring入門した時に感じた「少し基本を崩すと次に踏み出す足場がなくなる」ような感覚はなかった。

html/cssの基本を身につけたのがでかいのかなー。我ながらフロントの知識0からWEBフレームワーク進めようとしていたのはちょっと・・

R2I5wR2I5w

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)

表形式で表示、検索窓、フィルター窓追加。

R2I5wR2I5w

画像アップロード等の取り扱いを学んだ(開発環境限定)。このスクラップの本筋ではないが、こういうのはテンション管理も大事だと思うのでポスト。

R2I5wR2I5w

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は、テキストとバイナリデータの両方を含むことができる
R2I5wR2I5w

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の詳細ページ
R2I5wR2I5w

Dockerの便利さを実感してきた。

環境構築に躓くことが格段に減る。シンプルな環境なら秒で用意(は言い過ぎか)できるのよき。

R2I5wR2I5w

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
R2I5wR2I5w

まあ今のところこの理解で困っていないので、大丈夫そうだ。

R2I5wR2I5w

Djangoが必要としているSQLが何であるかをスクリーンに表示

  • python manage.py sqlmigrate polls 0001
    ※よしなに読み替えて!
R2I5wR2I5w

マイグレーションを作成したりデータベースにふれることなく、プロジェクトに何か問題がないか確認

  • python manage.py check
R2I5wR2I5w

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というものがあるらしい。キャッシュの動作確認とかもしてみたい。
R2I5wR2I5w

テンプレートの名前空間

作ったテンプレートを (polls という別のサブディレクトリを作らずに) 直接 polls/templates の中に置いてもいいのではないか、と思うかもしれませんね。しかし、それは実際には悪い考えです。Django は、名前がマッチした最初のテンプレートを使用するので、もし 異なる アプリケーションの中に同じ名前のテンプレートがあった場合、Django はそれらを区別することができません。そのため、Django に正しいテンプレートを教えてあげる必要がありますが、一番簡単な方法は、それらに 名前空間を与える ことです。アプリケーションと同じ名前をつけた もう一つの ディレクトリの中にテンプレートを置いたのは、そういうわけなのです。

なるほど。

R2I5wR2I5w

違和感

  • 以下の書き方に違和感。
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)
R2I5wR2I5w
  • これも

    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})
R2I5wR2I5w

URL 名の名前空間

polls/urls.py
 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"),
 ]
polls/templates/polls/index.html
- <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>
R2I5wR2I5w

静的ファイル

settings.py
# プロジェクトのsettings.py
STATIC_URL = 'static/'

# 開発環境での静的ファイル設定
STATICFILES_DIRS = [
    BASE_DIR / "static",
]

# 本番環境用(必要な場合)
STATIC_ROOT = BASE_DIR / 'staticfiles'
R2I5wR2I5w

Django のソースファイルの場所確認

$ python -c "import django; print(django.__path__)"
R2I5wR2I5w

Hoge.objects.all()

  • もし結果が0でも例外発生しない。空のリストが戻ってくる
R2I5wR2I5w

トランザクションのデフォルト設定をトランザクションの有効範囲をビューの開始から終了までにする

settings.py
DATABASES = {
    'default': {
        ..
        'ATOMIC_REQUESTS': True,  # 追加
    }
}
R2I5wR2I5w

SecurityMiddleware

  • 「HTTP」でアクセスした場合に 「HTTPS」 で始まる URL に自動的にリダイレクトするようにレ スポンスを返す(デフォではfalse)
settings.py
SECURE_SSL_REDIRECT = True 
このスクラップは5日前にクローズされました