なぜPythonを使うと爆速でアプリ開発できるのか
効率的でスマートなアプリ開発はプロジェクトの成功率を大幅に向上させます。PythonとDjangoでアプリ開発をすることで効率的なアプリ開発を実現できます。
そこで前半はPythonとDjangoの組み合わせがいい理由を、後半は実際に投票アプリを作成する方法を記載していきます。
(本サイトはアフィリエイト広告を利用しています)
PythonとDjangoの組み合わせは相性がいい
Pythonはシンプルな構文と高い可読性を持ち、Djangoは効率的なWebアプリケーション開発を支援するフレームワークです。
開発者はコードを迅速に書くことができ、煩雑な構文や冗長な宣言を避けることができます。
開発したいロジックに集中できる
Djangoはデータベースアクセス、フォーム処理、ユーザー認証などの共通の開発タスクを自動化し、開発者がこれらの基盤作業に時間を費やすことなく、開発したいアプリケーションロジックに集中できる環境を提供しています。
強力なセキュリティがついている
さらに、Djangoの強力なセキュリティ機能は、アプリケーションの安全性を確保するのに役立ちます。Djangoはクロスサイトスクリプティング(XSS)、SQLインジェクション、CSRF(クロスサイトリクエストフォージェリ)などの一般的な脆弱性からアプリケーションを保護するためのツールを提供しています。
ここからは実際に投票アプリを開発してDjangoの爆速を体験してみましょう!
Dockerで環境構築
以下のようにDockerを使って環境構築をします。
django-tutorialの中にweb-1とdb-1というコンテナを作成し、web-1コンテナの中でアプリを投票アプリを作成していきます。
Docker準備
デスクトップに「Django-Tutorial」フォルダを作成します。
requirement.txt に以下のように必要なライブラリを記載します。
requirements.txt
---------------------------------------------------------------------------
asgiref==3.7.2
Django==4.2.4
psycopg2==2.9.7
psycopg2-binary==2.9.7
sqlparse==0.4.4
typing_extensions==4.7.1
Dockerfileにコンテナへの設定を記載します。
Dockerfile
---------------------------------------------------------------------------
FROM python:3.11
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/
docker-compose.yml に作成するコンテナを記載します。
docker-compose.yml
---------------------------------------------------------------------------
version: "3"
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD: "password123"
web:
build: .
command: python3 poll_project/manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
ターミナルで以下のコマンドを実行してpoll_projectフォルダを作成します。
Django-Tutorial % docker-compose run web django-admin startproject poll_project
以下のようなフォルダが作成されます、
poll_project/
manage.py
poll_project/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
poll_project/poll_project/settings.pyのDB設定を変更
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'password123',
'HOST': 'db',
'PORT': 5432,
}
}
ついでにタイムゾーンと言語を変更
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
投票機能を作成する(準備)
docker exec -it django-tutorial-web-1 bash
poll_projectフォルダへ移動して投票機能となるアプリ機能を作成します。
root@2a64f6359306:/code# cd poll_project/
root@2a64f6359306:/code/poll_project# python3 manage.py startapp polls
以下のようなアプリフォルダが作成されます。
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
pollsが機能するか確認する
アプリ機能が正常に機能するか確認します。
polls/views.py を開いて、以下のように記載してください。
poll_project/polls/views.py
---------------------------------------------------------------------------
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. これはpollsのViewが表示されています。")
polls/urls.py ファイルを作成し、viewへ遷移するように記載します。
poll_project/polls/urls.py
---------------------------------------------------------------------------
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index")
]
poll_project/urls.py ファイルに投票アプリへ接続したときにpolls/urls.pyへ遷移するように記載します。
poll_project/poll_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),
]
http://localhost:8000/polls/ をブラウザで表示すると画像のようになっていれば正しく表示されています。
投票機能を作成する(作成)
ここからは投票機能を作成していきます。
投票アプリ概要
ユーザが投票したり結果を表示したりできる公開用サイト
投票項目の追加、変更、削除を行うための管理 (admin) サイト
pollsの機能を追加する
pollsの機能を使えるようにするため、poll_project/settings.pyにpollsを追加します。
poll_project/poll_project/settings.py
---------------------------------------------------------------------------
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'polls'
]
データベースを設定
コンテナで作成しているpostgresqlにDjangoでしようするテーブルの設定を反映していきます。
ターミナルでDjango-Tutorialフォルダにいることを確認し、以下のコマンドを実行します。
Django-Tutorial % docker exec -it django-tutorial-web-1 bash
poll_projectフォルダに移動して以下のコマンドを実行することでDjangoでしようするテーブルが反映されます。
root@2a64f6359306:/code# cd poll_project/
root@2a64f6359306:/code/poll_project# python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
Djangoで設定している、あるいは設定したコードをデータベースに反映することをマイグレーションといいます。
モデルの作成
ここからはデータベースと連携するファイルであるmodels.pyの編集をします。
pollsのmodels.pyに以下のように記載します。
poll_project/polls/models.py
---------------------------------------------------------------------------
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
データのつながりは以下のようになっています。
idはDjangoが自動で付与してくれます。もちろん、自身でquestion_idのように設定することもできます。
ChoiceのquestionがintになっているのはQuestionのidが外部キーとして入ってくるからです。
モデルをデータベースに反映
models.pyで設定したモデルをもとにマイグレーションファイルを作成します。
root@2a64f6359306:/code/poll_project# python manage.py makemigrations polls
Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
マイグレーションファイルが作成されたのを確認し、以下のコマンドでマイグレーションの内容をデータベースに反映します。
root@2a64f6359306:/code/poll_project# python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0001_initial... OK
投票の管理機能を追加
ここではDjangoの管理機能で投票を管理する方法を記載します。
管理者ユーザーの作成
Djangoにはデフォルトでユーザーを管理する機能があります。
以下のコマンドでユーザーを作成します。
root@2a64f6359306:/code/poll_project# python manage.py createsuperuser
ユーザー名 (leave blank to use 'root'): adminuser
メールアドレス:
Password: password
Password (again): password
このパスワードは一般的すぎます。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
今回はチュートリアルということなので簡単なユーザー名とパスワードを使用しましたが、本番環境にもデプロイする可能性がある場合は推測しにくいものをしようしてください。
次はブラウザで http://127.0.0.1:8000/admin/ にアクセスします。
以下のような admin のログイ ン画面が表示されます。
ログインするとDjangoの管理画面が表示されます。
管理画面でデータを編集できるようにする
管理画面でモデルで定義されているデータを登録できるようにadmin.pyを以下のように記載します。
poll_project/polls/admin.py
---------------------------------------------------------------------------
from django.contrib import admin
from .models import Question, Choice
admin.site.register(Question)
admin.site.register(Choice)
もう一度 http://127.0.0.1:8000/admin/ にアクセスします。
すると、管理画面に作成したモデルが表示されました。
モデルの日本語化
しかし、モデルをそのまま表示しているだけになっているので、日本語で表示されるように修正します。
アプリ機能のタイトルが「POLLS」になっているので、「投票調査」に変更します。
poll_project/polls/apps.py
---------------------------------------------------------------------------
from django.apps import AppConfig
class PollsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'polls'
verbose_name = '投票調査'
モデル名が表示されている部分を日本語表示にしていきます。
poll_project/polls/models.py
---------------------------------------------------------------------------
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200, verbose_name='質問')
pub_date = models.DateTimeField( verbose_name='発表日')
def __str__(self):
return self.question_text
class Meta:
verbose_name = '質問'
verbose_name_plural = '質問'
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE,verbose_name='質問')
choice_text = models.CharField(max_length=200, verbose_name='選択理由')
votes = models.IntegerField(default=0, verbose_name='投票数')
def __str__(self):
return self.choice_text
class Meta:
verbose_name = '選択理由'
verbose_name_plural = '選択理由'
http://127.0.0.1:8000/admin/ にアクセスすると日本語化されていることがわかります。
データを追加
投票調査の質問にある「+追加」をクリックし、質問を追加します。
質問を追加すると質問一覧に質問が表示されます。
同じように選択理由も追加してみます。
管理画面で選択理由の右側にある「+追加」をクリックして選択理由を追加します。
先ほど追加した質問を選択してそれぞれの入力欄を記入します。
保存をクリックすると選択理由が保存されます。
投票機能を作成
ここからはユーザーが操作する画面を作成していきます。
アクセスフロー全体図
アクセスは以下のように処理されます。
ブラウザからurls.pyを経由してviewsにアクセスを振り分けます。
viewsはmodelsにアクセスして必要なデータをDBから持ってきてもらいます。
データを取得するとtemplatesにデータを埋め込んでブラウザに表示します。
この処理の方法をMVTモデルといいます。
プロジェクトのルーティングを作成
ブラウザから最初にアクセスされたリクエストを振り分ける部分を作成します。
poll_project/poll_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),
]
アプリのルーティングを作成
プロジェクトから受け取ったリクエストをどの機能(view)に送るかを記載します。
poll_project/polls/urls.py
---------------------------------------------------------------------------
from django.urls import path
from . import views
app_name = "polls"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path("<int:pk>/", views.DetailView.as_view(), name="detail"),
path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]
アプリの処理を作成
一覧表示処理(IndexView)、詳細表示処理(DetailView)、結果表示処理(ResultsView)、投票処理(vote)を記載します。
poll_project/polls/views.py
---------------------------------------------------------------------------
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from django.utils import timezone
from .models import Choice, Question
class IndexView(generic.ListView):
template_name = "polls/index.html"
context_object_name = "latest_question_list"
def get_queryset(self):
# Return the last five published questions (not including those set to be published in the future).
return Question.objects\
.filter(pub_date__lte=timezone.now())\
.order_by("-pub_date")[:5]
class DetailView(generic.DetailView):
model = Question
template_name = "polls/detail.html"
class ResultsView(generic.DetailView):
model = Question
template_name = "polls/results.html"
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST["choice"])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(
request,
"polls/detail.html",
{
"question": question,
"error_message": "You didn't select a choice.",
},
)
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/results.html", {"question": question})
アプリのモデルを作成
DBに設定するデータ型や管理画面で表示する文言などを記載します。
poll_project/polls/models.py
---------------------------------------------------------------------------
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200, verbose_name='質問')
pub_date = models.DateTimeField( verbose_name='発表日')
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Meta:
verbose_name = '質問'
verbose_name_plural = '質問'
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE,verbose_name='質問')
choice_text = models.CharField(max_length=200, verbose_name='選択理由')
votes = models.IntegerField(default=0, verbose_name='投票数')
def __str__(self):
return self.choice_text
class Meta:
verbose_name = '選択理由'
verbose_name_plural = '選択理由'
アプリのテンプレートを作成
ブラウザに表示する画面を作成します。
一覧画面
poll_project/polls/templates/polls/index.html
---------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>投票</title>
<!-- Bootstrap CSS link -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.mt-4 {
margin-top: 1.5rem;
}
.list-group-item {
display: flex;
justify-content: space-between;
align-items: center;
border: none;
}
.list-group-item a {
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<h1 class="mt-4">投票一覧</h1>
<hr>
{% if latest_question_list %}
<ul class="list-group">
{% for question in latest_question_list %}
<li class="list-group-item">
<a class="btn btn-primary" href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
</div>
<!-- Bootstrap JS scripts (optional) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
詳細画面
poll_project/polls/templates/polls/detail.html
---------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>質問詳細</title>
<!-- Bootstrap CSS link -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.mt-4 {
margin-top: 1.5rem;
}
.form-check-label {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1 class="mt-4">{{ question.question_text }}</h1>
<hr>
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
<legend>
<h2>{{ question.question_text }}</h2>
</legend>
{% if error_message %}
<p class="text-danger"><strong>{{ error_message }}</strong></p>
{% endif %}
{% for choice in question.choice_set.all %}
<div class="form-check">
<input class="form-check-input" type="radio" name="choice" id="choice{{ forloop.counter }}"
value="{{ choice.id }}">
<label class="form-check-label" for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label>
</div>
{% endfor %}
</fieldset>
<button type="submit" class="btn btn-primary mt-3">投票する</button>
</form>
<a href="{% url 'polls:index'%}" class="btn btn-secondary mt-3">投票一覧に戻る</a>
</div>
<!-- Bootstrap JS scripts (optional) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
投票結果画面
poll_project/polls/templates/polls/results.html
---------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>結果</title>
<!-- Bootstrap CSS link -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.mt-4 {
margin-top: 1.5rem;
}
.list-group-item {
display: flex;
justify-content: space-between;
align-items: center;
border: none;
}
.btn-secondary {
margin-top: 0.5rem;
}
</style>
</head>
<body>
<div class="container">
<h1 class="mt-4">{{ question.question_text }}</h1>
<hr>
<ul class="list-group">
{% for choice in question.choice_set.all %}
<li class="list-group-item">
{{ choice.choice_text }} -- {{ choice.votes }} 票
</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}" class="btn btn-secondary mt-3">もう一度投票する</a>
<a href="{% url 'polls:index'%}" class="btn btn-secondary mt-3">投票一覧に戻る</a>
</div>
<!-- Bootstrap JS scripts (optional) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
もっと学びたい方へ
DjangoはPythonのWebフレームワークで1番使用されていて、中・大規模の開発に向いているフレームワークです。
Djangoを使って設計から開発、デプロイまで経験することでよりエンジニアとしてステップアップできます。
Django・Pythonでのバックエンドはもちろん、フロントエンド、インフラを理解しておくことでエンジニアの価値が向上します。
詳細はこちら
未経験からのITエンジニア
IT技術の進化速度が速くなっていっている今、IT技術にアンテナを張っておくことはとても重要です。エンジニアになろうとしている方やそうではない方もITスキルを磨くことで問題解決能力や論理的思考を養うことができます。複雑な課題に対処し、効果的な解決策を見つける能力はどの職種でも重宝されます。
そこで、エンジニア未経験の時にあったらよかったなと思っていた講座をマガジンにまとめました!アプリの基礎がわかるTodoアプリの開発からGit&GitHubでのチーム開発までエンジニアに必要な知識が詰まっています!
エンジニアリングの第一歩として使ってみてください!
「売上げアップに効果的」なキャッチコピーやテキストを作成してくれる国内最大級の「AIコピーライティングツール」
【Catchy】
会議・ミーティングの内容をリアルタイムで文字に起こすAI自動文字起こしサービス
【Notta】
ブログの記事制作にかかる時間を1/10で制作できる高品質SEO記事生成AIツール
【Value AI Writer byGMO】