cloud9_note

cloud9に限らないメモ

View on GitHub

Django

前提

このページでは下記で記載する。

ビューの作成は、既存のものを使用したほうが良い。下記を参照。

init

Pythonバージョンとの整合性が存在する。「とりあえず最新」とかやると失敗する。
ここでは下記で導入する。

# バージョン確認
python --version
sqlite3 --version

# ディレクトリ作成
project=project
mkdir project

cd project

# venv作成
python -m venv django_venv

# venv起動
source django_venv/bin/activate

# djangoインストール
python -m pip install Django
python -m django --version

# プロジェクト作成
django-admin startproject project
# 何かをpip install/uninstallしたら行う。
pip freeze > requirements.txt
# プロジェクトとして、projectとproject配下に管理アプリとして、projectが作成される。
ls
django_venv  project  requirements.txt
(django_venv) ubuntuuser@ubuntuuser-B660M:~/environment/tmp_django $ ls project
manage.py  project
(django_venv) ubuntuuser@ubuntuuser-B660M:~/environment/tmp_django $ 

参考

起動

cd project
source django_venv/bin/activate
pip install -r requirements.txt

cd project
python manage.py runserver
# 外からアクセスできるようにする場合は下記。
python manage.py runserver 0.0.0.0:8000

アプリケーションの作成

python manage.py startapp app

ビュー作成(仮)

# app/views.py 

from django.http import HttpResponse

def index(request):
    return HttpResponse('Hello World.')

URLパス設定

touch app/urls.py
# app/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
# project/urls.py
# プロジェクト管理用のファイルに、新しく作成したアプリを登録する。

# 追記
from django.urls import include, path

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

project/settings.py

# 下記を追加
# 作成したアプリの名前を追加する。
# ChatGPTの回答なので、間違っている可能性はあるが、必要に応じて、app.ファイル名.クラス名を記載すれば良い。
INSTALLED_APPS = [
    'app'
]

テンプレート作成

# app=
mkdir app/templates/app
# htmlfile=
touch app/templates/app/${htmlfile}
<!-- 任意の内容を書く。 -->
<!-- この項目では触れない。 -->

app/views.py

from django.shortcuts import render

def index(request):
    return render(request, 'app/index.html')

参考

テンプレートの作成例(listの表示)

テンプレート

<h1>Message List</h1>
{% if message_list %}
    <ul>
        {% for model in message_list %}
        <li>{{ model.message }}</li>
        {% endfor %}
    </ul>
{% else %}
<p>Message is not found.</p>
{% endif %}
<a href="add_message">Add Message</a>

app/views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.template import loader
from .models import Message

def index(request):
    message_list = Message.get_message_list()
    template = loader.get_template('usermodel/index.html')
    context = {
        'message_list': message_list,
    }
    return HttpResponse(template.render(context, request))

def add_message(request):
    return render(request, 'usermodel/add_message.html')

または下記。

from django.shortcuts import render
from .models import Message

def index(request):
    message_list = Message.get_message_list()
    context = {
        'message_list': message_list,
    }
    return render(request, 'usemodel/index.html', context)

def add_message(request):
    return render(request, 'usemodel/add_message.html')

staticファイル配置

cssなどの配置方法。

./project/project/settings.py

# settings.py

# 静的ファイルの基本パスを設定
STATIC_URL = '/static/'

# 静的ファイルを格納するディレクトリを指定
STATICFILES_DIRS = [
    BASE_DIR / 'app/static',  # ここをプロジェクトの構成に合わせて変更
]

テンプレートファイル

<!-- 例: Djangoテンプレート内でのCSSリンク -->
{% load static %}
<link href="{% static 'app/css/app.css' %}" rel="stylesheet">

ファイルアップロード

touch project/app/forms.py

app/forms.py

from django import forms

class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file = forms.FileField()

project/urls.py

from django.contrib import admin
from django.urls import include, path
import fileupload.views as fileupload

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

app/vies.py

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from django.http import HttpResponse

# Imaginary function to handle an uploaded file.
from .controller import handle_uploaded_file

# import logging
# logger = logging.getLogger('development')

def success(request):
    return render(request, 'fileupload/success.html')

def index(request):
    if request.method == 'POST':
        # POSTの場合はUploadされたファイルを処理して、fileupload/successにリダイレクトする。
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            file_obj = request.FILES['file']
            logger.info(file_obj.name)
            handle_uploaded_file(file_obj)
            return HttpResponseRedirect('/fileupload/success/')
    else:
        # POST以外の場合はUploadフォームを作成して、index.htmlを表示する。
        form = UploadFileForm()
    return render(request, 'fileupload/index.html', {'form': form})

app/controller.py

def handle_uploaded_file(f):
    pass
    # print(f)
    # with open('some/file/name.txt', 'wb+') as destination:
    #     for chunk in f.chunks():
    #         destination.write(chunk)

app/template/index.html

<h1>File upload</h1>

<form method="POST" enctype="multipart/form-data">
    <!-- ファイルの送信元を保証する -->
    <!-- バックスラッシュは不要。(Githubのビルドでエラーになるため追加。) -->
    {\% csrf_token \%}

    <!-- views.index内で生成されるUploadFileFormクラスのオブジェクトをレンダリングできるフォーマットに変換する。 -->
    <!-- バックスラッシュは不要。(Githubのビルドでエラーになるため追加。) -->
    \{\{ form.as_p \}\}

    <button type="submit">Upload</button>
</form>

app/template/success.html

<p>Success!</p>
<a href="/fileupload/">Go to index.</a>

参考

GetのURLの作成例

app/views.py

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(requests, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

app/urls.py

from django.urls import path

from .import views

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'),
]

参考

POST(form)

  1. forms.pyに記載する
  2. GET時にPython側でFormオブジェクトを作成する。
  3. htmlのclassはFormで定義する。
  4. {{ form.value }}
  5. idはid_valueで作成される。(getElementByIdの引数にはこの値を渡す)

views.py

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import PullRequestForm
# from django.http import HttpResponse

# Create your views here.

def index(request):
    return render(request, 'app/index.html')

def list_view(request):
    return render(request, 'app/list.html')

def register(request):
    if request.method == 'POST':
        form = PullRequestForm(request.POST)
        if form.is_valid():
        
            st_pull_request_url = form.cleaned_data['st_pull_request_url']
            print(st_pull_request_url)
            print('target_branch' in form.cleaned_data)
        else:
            print(form.errors)
        return HttpResponseRedirect('list')
    else:
        # getとして扱う
        form = PullRequestForm()
        return render(request, 'app/register.html', {'form': form})

forms.py

from django import forms

from dataclasses import dataclass

class PullRequestForm(forms.Form):
    repository = forms.CharField(
        label='repository',
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'readonly': True            
        }))
    title = forms.CharField(
        label='title',
        max_length=255,
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'readonly': True            
        }))
    st_pull_request_url = forms.CharField(
        label='st_pull_request_url',
        max_length=1000,
        required=True,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'oninput': 'updateInputState()'
        }))
    e2e_uat_pull_request_url = forms.CharField(
        label='e2e_uat_pull_request_url',
        max_length=1000,
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'readonly': True,
            'oninput': 'updateInputState()'
            }))
    main_pull_request_url = forms.CharField(
        label='main_pull_request_url',
        max_length=1000,
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'readonly': True,
            'oninput': 'updateInputState()'
        }))
    source_branch = forms.CharField(
        label='source_branch',
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'readonly': True
        }))
    target_branch = forms.CharField(
        label='target_branch',
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline',
            'readonly': True
        }))

    pull_request_id: int

regist.htmlテンプレート

{% extends "app/base.html" %}

{% block title %}登録{% endblock %}

{% block content %}

{% load static %}
<link href="{% static 'app/css/app.css' %}" rel="stylesheet">

<div class="container mx-auto mt-10">
    <form action="/app/register" method="post">
        {% csrf_token %}
        <div class="mb-4">
            <label for="repository" class="block text-gray-700 text-sm font-bold mb-2">リポジトリ:</label>
            {{ form.repository }}
        </div>
        <div class="mb-4">
            <label for="title" class="block text-gray-700 text-sm font-bold mb-2">タイトル:</label>
            {{ form.title }}
        </div>
        <div class="mb-4">
            <label for="st_pull_request_url" class="block text-gray-700 text-sm font-bold mb-2">STプルリクエストURL:</label>
            {{ form.st_pull_request_url }}
        </div>
        <div class="mb-4">
            <label for="e2e_uat_pull_request_url" class="block text-gray-700 text-sm font-bold mb-2">E2E UATプルリクエストURL:</label>
            {{ form.e2e_uat_pull_request_url }}
        </div>
        <div class="mb-4">
            <label for="main_pull_request_url" class="block text-gray-700 text-sm font-bold mb-2">メインプルリクエストURL:</label>
            {{ form.main_pull_request_url }}
        </div>
        <div class="flex mb-4">
            <div class="w-1/2 pr-2">
                <label for="source_branch" class="block text-gray-700 text-sm font-bold mb-2">ソースブランチ:</label>
                {{ form.source_branch }}
            </div>
            <div class="w-1/2 pl-2">
                <label for="target_branch" class="block text-gray-700 text-sm font-bold mb-2">ターゲットブランチ:</label>
                {{ form.target_branch }}
            </div>
        </div>
        <div class="mb-4">
            <input type="submit" value="登録" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
        </div>
    </form>
</div>

<script>
    function updateInputState() {
        // STプルリクエストURLに値が入力された
        const stUrl = document.getElementById('id_st_pull_request_url').value;
        setDisable('id_e2e_uat_pull_request_url', stUrl);

        // E2E UATプルリクエストURLに値が入力された
        const e2eUrl = document.getElementById('id_e2e_uat_pull_request_url').value;
        setDisable('id_main_pull_request_url', e2eUrl);

        // メインプルリクエストURLに値が入力された
        const mainUrl = document.getElementById('id_main_pull_request_url').value;

        const url = mainUrl || e2eUrl || stUrl;
        const repository = getRepositoryName(url);
        const pullRequestId = getPullRequestId(url);

        document.getElementById('id_repository').value = repository;

        const branches = getBranches(url);
        document.getElementById('id_target_branch').value = branches.target;
        document.getElementById('id_source_branch').value = branches.source;

    }
    function getRepositoryName(url) {
        // TODO : 後で書く
        return 'SampleRepository'
    }

    function getPullRequestId(url) {
        // TODO : 後で書く
        const removeWord = "https://dev.azure.com/ittimfn/SampleProject/_git/SampleProject/pullrequest/"
        return url.replace(removeWord, '')
    }

    function getBranches(url) {
        // TODO : 後で書く
        return {'source': 'develop', 'target': 'master'}
    }
    function setDisable(id, baseValue) {
        console.info(id , baseValue)
        const state = !baseValue;
        console.info(state);

        const element = document.getElementById(id);
        console.info(element.value)
        element.disabled = state;
        element.readOnly = state;
        if (state) {
            element.value = "";
        }
    }

</script>
{% endblock %}

Modelを使う

DB設定

project/settings.py

# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

app/models.py

from django.db import models

class Message(models.Model):
    message = models.CharField(max_length=256)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        '''Adminページに表示する文言を設定する'''
        return self.message

make migrations

python manage.py makemigrations app
python manage.py migrate

DBに登録する

from django.utils import timezone
from app.models import Message

# 登録したい値の設定
message = Message(message='hoge', pub_date=timezone.now())

# 登録
message.save()

DBに登録されているデータを取得する

from app.models import Message

# 全部
Message.objects.all()

# 条件をつける
Message.objects.filter(id=1)
Message.objects.filter(message__startswith='ho')

current_year = django.utils.timezone.now().year
Message.objects.get(pub_date__year=current_year)

Message.objects.get(pk=1)

選択肢を登録/全部取得/選択/削除する

親子関係があり、子供側に選択肢をもたせて、カウントする。
参考から引用。

import datetime

from django.db import models
from django.utils import timezone

# Create your models here.

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self) -> str:
        return self.question_text

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self) -> str:
        return self.choice_text
from polls.models import Choice, Question

q = Question.objects.get(pk=1)

# 登録
q.choice_set.create(choice_text='Not much', votes=0)
q.choice_set.create(choice_text='The sky', votes=0)
c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# 全部取得
q.choice_set.all()

# 順番を指定する
Question.objects.order_by('-pub_date')[:5]

# 件数取得
q.choice_set.count()

# 選択
c = q.choice_set.filter(choice_text='Just hacking again')

# 削除
c.delete()

参考

一覧を表示する

models.py

from django.db import models

from .forms import PullRequestForm

class PullRequestModel(models.Model):
    '''PR情報を管理するモデル'''
    repository = models.CharField(max_length=255)
    title = models.CharField(max_length=255)
    st_pull_request_url = models.CharField(max_length=1000)
    e2e_uat_pull_request_url = models.CharField(max_length=1000)
    main_pull_request_url = models.CharField(max_length=1000)
    
    source_branch = models.CharField(max_length=255)
    target_branch = models.CharField(max_length=255)
    
    pull_request_id = models.IntegerField()

    def set_Form(self, form: PullRequestForm):
        '''FormからModelに値を設定する'''
        self.repository = form.cleaned_data['repository']
        self.title = form.cleaned_data['title']
        self.st_pull_request_url = form.cleaned_data['st_pull_request_url']
        self.e2e_uat_pull_request_url = form.cleaned_data['e2e_uat_pull_request_url']
        self.main_pull_request_url = form.cleaned_data['main_pull_request_url']
        self.source_branch = form.cleaned_data['source_branch']
        self.target_branch = form.cleaned_data['target_branch']
        try:
            self.pull_request_id = form.get_id_from_pull_request_url()
        except (KeyError, ValueError):
            self.pull_request_id = 0
        

    def __str__(self):
        '''Adminページに表示する文言を設定する'''
        return self.message

views.py

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .models import PullRequestModel

from .forms import PullRequestForm
# from django.http import HttpResponse

# Create your views here.

def index(request):
    return render(request, 'app/index.html')

def list_view(request):
    pull_requests = PullRequestModel.objects.all()
    context = {
        'pull_requests': pull_requests
    }
    return render(context=context, request=request, template_name='app/list.html')

def register(request):
    if request.method == 'POST':
        form = PullRequestForm(request.POST)
        if form.is_valid():
            model = PullRequestModel()
            model.set_Form(form)
            model.save()
        else:
            print(form.errors)
        return HttpResponseRedirect('list')
    else:
        # getとして扱う
        form = PullRequestForm()
        return render(request, 'app/register.html', {'form': form})

app/list.html

{% extends "app/base.html" %}

{% block title %}一覧{% endblock %}

{% block content %}
<div class="container mx-auto mt-10">
    <table class="table-auto w-full">
        <thead>
            <tr>
                <th class="px-4 py-2">リポジトリ</th>
                <th class="px-4 py-2">タイトル</th>
                <th class="px-4 py-2">STプルリクエストURL</th>
                <th class="px-4 py-2">E2E UATプルリクエストURL</th>
                <th class="px-4 py-2">メインプルリクエストURL</th>
                <th class="px-4 py-2">ソースブランチ</th>
                <th class="px-4 py-2">ターゲットブランチ</th>
                <th class="px-4 py-2">プルリクエストID</th>
            </tr>
        </thead>
        <tbody>
            <!-- ここにデータを動的に表示 -->
            {% for pr in pull_requests %}
            <tr>
                <td class="border px-4 py-2">{{ pr.repository }}</td>
                <td class="border px-4 py-2">{{ pr.title }}</td>
                <td class="border px-4 py-2">{{ pr.st_pull_request_url }}</td>
                <td class="border px-4 py-2">{{ pr.e2e_uat_pull_request_url }}</td>
                <td class="border px-4 py-2">{{ pr.main_pull_request_url }}</td>
                <td class="border px-4 py-2">{{ pr.source_branch }}</td>
                <td class="border px-4 py-2">{{ pr.target_branch }}</td>
                <td class="border px-4 py-2">{{ pr.pull_request_id }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>
{% endblock %}

レイアウト(ビュー)を共通化する

「継承」できる。

project/app/templates/app/base.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}デフォルトタイトル{% endblock %}</title>
    <!-- Tailwind CSSの場合 -->
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
    <!-- Bootstrapの場合、以下の行をコメントアウトし、上の行を削除してください -->
    <!-- <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> -->
</head>
<body>
    <nav class="bg-gray-800 p-4 text-white">
        <div class="container mx-auto">
            <a href="/app/list" class="font-semibold mr-4">ホーム</a>
            <a href="/app/register" class="font-semibold">登録</a>
    </nav>

    {% block content %}
    {% endblock %}

</body>
</html>

project/app/templates/app/register.html

{% extends "app/base.html" %}

{% block title %}登録{% endblock %}

{% block content %}

{% load static %}
<link href="{% static 'app/css/app.css' %}" rel="stylesheet">

<div class="container mx-auto mt-10">
    <form action="/app/register" method="post">
        {% csrf_token %}
        <div class="mb-4">
            <label for="title" class="block text-gray-700 text-sm font-bold mb-2">リポジトリ:</label>
            <input type="text" id="repository" name="repository" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" disabled>
        </div>
        <div class="mb-4">
            <label for="title" class="block text-gray-700 text-sm font-bold mb-2">タイトル:</label>
            <input type="text" id="title" name="title" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" disabled>
        </div>
        <div class="mb-4">
            <label for="st_pull_request_url" class="block text-gray-700 text-sm font-bold mb-2">STプルリクエストURL:</label>
            <input type="text" id="st_pull_request_url" name="st_pull_request_url" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" oninput="updateInputState()">
        </div>
        <div class="mb-4">
            <label for="e2e_uat_pull_request_url" class="block text-gray-700 text-sm font-bold mb-2">E2E UATプルリクエストURL:</label>
            <input type="text" id="e2e_uat_pull_request_url" name="e2e_uat_pull_request_url" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" disabled oninput="updateInputState()">
        </div>
        <div class="mb-4">
            <label for="main_pull_request_url" class="block text-gray-700 text-sm font-bold mb-2">メインプルリクエストURL:</label>
            <input type="text" id="main_pull_request_url" name="main_pull_request_url" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" disabled>
        </div>
        <div class="flex mb-4">
            <div class="w-1/2 pr-2">
                <label for="source_branch" class="block text-gray-700 text-sm font-bold mb-2">ソースブランチ:</label>
                <input type="text" id="source_branch" name="source_branch" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline input-disabled" readonly>
            </div>
            <div class="w-1/2 pl-2">
                <label for="target_branch" class="block text-gray-700 text-sm font-bold mb-2">ターゲットブランチ:</label>
                <input type="text" id="target_branch" name="target_branch" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline input-disabled" readonly>
            </div>
        </div>
        <div class="mb-4">
            <input type="submit" value="登録" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
        </div>
    </form>
</div>

<script>
    function updateInputState() {
        // STプルリクエストURLに値が入力された
        const stUrl = document.getElementById('st_pull_request_url').value;
        document.getElementById('e2e_uat_pull_request_url').disabled = !stUrl;

        // E2E UATプルリクエストURLに値が入力された
        const e2eUrl = document.getElementById('e2e_uat_pull_request_url').value;
        document.getElementById('main_pull_request_url').disabled = !e2eUrl;

        // メインプルリクエストURLに値が入力された
        const mainUrl = document.getElementById('main_pull_request_url').value;

        const url = mainUrl || e2eUrl || stUrl;
        const repository = getRepositoryName(url);
        const pullRequestId = getPullRequestId(url);

        document.getElementById('repository').value = repository;
        console.info(repository, pullRequestId);

    }
    function getRepositoryName(url) {
        // TODO : 後で書く
        return 'SampleRepository'
    }

    function getPullRequestId(url) {
        // TODO : 後で書く
        const removeWord = "https://dev.azure.com/ittimfn/SampleProject/_git/SampleProject/pullrequest/"
        return url.replace(removeWord, '')
    }
</script>
{% endblock %}

404を送出する

from django.shortcuts import render, get_object_or_404

def detail(request, question_id):
    template_path = 'polls/detail.html'
    question = get_object_or_404(Question, pk=question_id)

    return render(request, template_path, {'question': question})

Django admin

python manage.py createsuperuser
# ユーザ名、メールアドレス、パスワードを入力する。

AdminにModelを追加する

app/admin.py

from django.contrib import admin

# Register your models here.

from .models import Message

admin.site.register(Message)

bootstrap導入

pip install

# venv起動済みであることを確認してから実行。
pip install django-bootstrap5

project/settings.py

# 追加
INSTALLED_APPS = [
    'django_bootstrap5',
]

テンプレートファイル

<!-- 1行目に記載 -->
<!-- バックスラッシュは不要。(Githubの都合で記載。) -->
\{\% load django_bootstrap5 \%\}

<head>
    \{\% bootstrap_css \%\}
    \{\% bootstrap_javascript \%\}
</head>

デフォルトで適用できるbootstrap_formがあるので、調査して使うこと。

参考

CSRF検証でエラーになった場合

project/settings.py に下記を追記する。

CSRF_TRUSTED_ORIGINS = ['https://3a5caa305fbe48f8b96fbf040031a010.vfs.cloud9.ap-northeast-1.amazonaws.com']

参考

manage.py

参考