PythonのWebフレームワークであるDjangoについて、概要を勉強するために「Django Girls のチュートリアル」をやってみました。このチュートリアルでは、ブログのWebアプリケーションを作成することでDjangoの概要とWebアプリケーションの仕組みを学ぶことができます。実装する量も少なく挫折することなく簡単に実装できるので、Djangoの概要を知りたい初心者向けの内容になっています。
*Django とは
DjangoはPythonのWebアプリケーションフレームワークです。ユーザー認証や管理者用の画面、ファイルアップロードなどを簡単に実装することができるようになっています。*参考
*環境
- MacOS
- Python 3.6.3
- Django 2.0.9
*環境構築
任意の場所に作業用ディレクトリを作成して、そこに仮想環境を作成します。$ mkdir djangogirls
$ cd djangogirls/
$ python3 -m venv myvenv
$ source myvenv/bin/activate
(myvenv)~/djangogirls$
Djangoをインストールするために、最新のpipをインストールします。
インストールが必要なパッケージリストを書くための
requirements.txt
を新規作成します。$ python3 -m pip install --upgrade pip
$ touch requirements.txt
作成した
requirements.txt
に下記を追加します。Django~=2.0.6
requirements.txt
の内容をインストールします。pip list
でインストールしたパッケージの確認ができます。$ pip install -r requirements.txt
$ pip list
Package Version
---------- -------
Django 2.0.9
pip 18.1
pytz 2018.7
setuptools 28.8.0
*プロジェクト作成
Djangoプロジェクトを作成します。$ django-admin startproject mysite .
$ ls
manage.py mysite myvenv requirements.txt
プロジェクトを作成すると、下記構成でファイルが作成されます。
djangogirls
├───manage.py
├───mysite
│ ├ settings.py <-- webサイトの設定
│ ├ urls.py <-- urlの設定
│ ├ wsgi.py
│ └ __init__.py
└───requirements.txt
hostや言語などの設定を修正します。
<settings.py>
# 修正
ALLOWED_HOSTS = ['127.0.0.1', '.pythonanywhere.com']
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
# 追加 (静的ファイルの追加)
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
*データベースの設定
今回はサーバーを使わないでファイルベースでデータ管理をする sqlite3 を使います。(初期設定をそのまま使います)<settings.py>
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
データベースを作成します。
$ python manage.py migrate
*Webサーバーの起動
下記コマンドを実行してWebサーバーを起動します。$ python manage.py runserver
下記URLにアクセスするとDjangoの画面が表示されます。
http://127.0.0.1:8000/
*アプリケーションの作成
作成したプロジェクトの中に複数のプロジェクトをもつ構造になっています。プロジェクトの中で下記コマンドを実行して、
blog
アプリケーションを作成します。$ python manage.py startapp blog
作成したアプリケーションは下記構成になっています。
blog
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
設定に作成したアプリケーション名を追加します。
<settings.py>
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', <-- 追加
]
*モデルの作成
ブログで作成した記事を保存するためのモデルを作成します。既に作成されている
models.py
に、モデル名のクラスを追加します。CharField
は桁数の制限ありテキスト、TextField
は桁数の制限なしテキストです。<models.py>
from django.db import models
from django.utils import timezone
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
モデルのためのテーブルを作成します。
$ python manage.py makemigrations blog
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Post
データベースにテーブルを追加します。
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OK
*管理者用ページの作成
Djangoでは管理者用のページがデフォルトで用意されています。既に作成されている
admin.py
に下記内容を書き込みます。<admin.py>
from django.contrib import admin
from .models import Post
admin.site.register(Post)
Webサーバーを起動します。
$ python manage.py runserver
下記URLにアクセスすると管理者用の画面が表示されます。
http://127.0.0.1:8000/admin/
管理者にするスーパーユーザーを作成します。
名前やパスワードを聞かれるので適当に入力します。
$ python manage.py createsuperuser
Username: admin
Email address: admin@admin.com
Password:
Password (again):
Webサーバーを起動して管理者画面を表示し、作成したユーザーでログインします。
$ python manage.py runserver
http://127.0.0.1:8000/admin/*URLを追加
初期表示用のURLを追加します。blog.urls
をインポートするよう設定します。<mysite/urls.py>
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')), <-- 追加
]
アプリケーション固有のURLでは、
post_list
を表示するよう設定します。<blog/urls.py>
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
]
*HTMLの追加
画面レイアウト用のディレクトリtemplate,blog
を新規作成します。blog
└ templates <-- 追加
└ blog <-- 追加
blog
ディレクトリ配下に、初期表示用の画面を新規作成します。<blog/templates/blog/post_list.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Girls blog</title>
</head>
<body>
<div>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div>
<p>published: 14.06.2014, 12:14</p>
<h2><a href="">My first post</a></h2>
<p>Aenean eu leo quam. こんにちは</p>
</div>
<div>
<p>公開日: 2014/06/14, 12:14</p>
<h2><a href="">My Second post</a></h2>
<p>よろしくお願いします。</p>
</div>
</body>
</html>
ブラウザで表示すると下記の画面になります。
*テーブルデータの操作
Djangoではクエリセットを使うことで、データベースのデータを参照したり、データを追加するといった操作を行うことができます。下記コマンドを実行すると、Djangoのインタラクティブコードが実行します。
$ python manage.py shell
## オブジェクトの確認
>>> from blog.models import Post
>>> Post.objects.all()
<QuerySet [<Post: テスト>]>
## ユーザーの確認
>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: admin>]>
>>> me = User.objects.get(username='admin')
## オブジェクトの作成
>>> Post.objects.create(author=me, title='Sample title', text='Test')
<Post: Sample title>
>>> Post.objects.all()
<QuerySet [<Post: テスト>, <Post: Sample title>]>
初期画面にデータを表示するためのメソッドを追加します。
<blog/views.py>
from django.shortcuts import render
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
*テンプレートの作成
初期表示用の画面を新規作成します。<blog/template/blog/post_list.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Girls blog</title>
</head>
<body>
<div>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
{% for post in posts %}
<div>
<p>published: {{ post.published_date }}</p>
<h1><a href="">{{ post.title }}</a></h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
</body>
</html>
*Bootstrapを追加
画面レイアウトを美しくするためのフレームワークBootstrap
を追加します。先ほど作成した
post_list.html
に<link rel="stylesheet"...
の2行を追加します。<blog/template/blog/post_list.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 追加 start -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<!-- 追加 end -->
<title>Django Girls blog</title>
</head>
<body>
<div>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
{% for post in posts %}
<div>
<p>published: {{ post.published_date }}</p>
<h1><a href="">{{ post.title }}</a></h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
</body>
</html>
*静的CSSファイルの作成
Djangoは自動的にstatic
フォルダを参照するようになっているので、プロジェクトに依存しないCSSファイルなどはstatic
ディレクトリに置きます。今回は
blog
ディレクトリ配下にstatic
ディレクトリを追加します。blog
├ migrations
├ static <-- 追加
└ templates
css
ディレクトリを新規作成し、その中にblog.css
を作成して下記を書き込みます。<blog/static/css/blog.css>
h1 a {
color: #FCA205;
font-family: 'Lobster';
}
body {
padding-left: 15px;
}
.page-header {
background-color: #ff9400;
margin-top: 0;
padding: 20px 20px 20px 40px;
}
.page-header h1, .page-header h1 a, .page-header h1 a:visited, .page-header h1 a:active {
color: #ffffff;
font-size: 36px;
text-decoration: none;
}
.content {
margin-left: 40px;
}
h1, h2, h3, h4 {
font-family: 'Lobster', cursive;
}
.date {
color: #828282;
}
.save {
float: right;
}
.post-form textarea, .post-form input {
width: 100%;
}
.top-menu, .top-menu:hover, .top-menu:visited {
color: #ffffff;
float: right;
font-size: 26px;
margin-right: 20px;
}
.post {
margin-bottom: 70px;
}
.post h1 a, .post h1 a:visited {
color: #000000;
}
post_list.html
でCSSを読み込むための設定をします。static
ディレクトリを読み込むために{% load static %}
をファイルの最初に追加します。また、
<link rel="stylesheet"...
にblog.css
を読み込むための設定を追加します。<post_list.html>
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link href="//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}"> <-- 追加
<title>Django Girls blog</title>
</head>
<body>
<div class="page-header">
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% for post in posts %}
<div class="post">
<p>published: {{ post.published_date }}</p>
<h1><a href="">{{ post.title }}</a></h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
</div>
</div>
</div>
</body>
</html>
*基本テンプレートを作成
各画面に共通する部分は基本テンプレートを使い、異なる部分だけ別で拡張するようにします。template/blog
配下にbase.html
を新規作成して下記を書き込みます。<blog/templates/blog/base.html>
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link href="//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
<title>Django Girls blog</title>
</head>
<body>
<div class="page-header">
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
初期表示用の画面で
base.html
を読み込む設定を追記し、base.html
に書かれている内容を削除します。<blog/templates/blog/post_list.html>
{% extends 'blog/base.html' %}
{% block content %}
{% for post in posts %}
<div class="post">
<p>published: {{ post.published_date }}</p>
<h1><a href="">{{ post.title }}</a></h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock %}
*詳細ページへのリンクを追加
タイトルをクリックすると詳細ページに遷移するようにします。{% url 'post_detail' pk=post.pk %}"
でviews.py
のpost_detail()
を呼び出すようにしていますが、ここはこのあと実装します。<blog/templates/blog/post_list.html>
{% extends 'blog/base.html' %}
{% block content %}
{% for post in posts %}
<div class="post">
<p>published: {{ post.published_date }}</p>
<!-- 追加 start -->
<h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1>
<!-- 追加 end -->
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock %}
views.py
のpost_detail()
を呼び出したいため、urls.py
に設定を追加します。<blog/urls.py>
from django.urls import path
from . import views <-- 追加
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'), <-- 追加
]
詳細画面を表示するためのメソッド
post_detail()
を追加します。<blog/views.py>
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
# 追加
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
詳細を表示するための画面を新規作成します。
<blog/templates/blog/post_detail.html>
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endblock %}
*新規追加画面の作成
記事を新規で投稿するためのフォーム画面を作成します。forms.py
を新規作成して下記を書き込みます。<blog/forms.py>
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
初期表示画面に、追加用のプラス記号のボタンを追加します。
<blog/template/blog/base.html>
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link href="//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
<title>Django Girls blog</title>
</head>
<body>
<div class="page-header">
<!-- 追加 start -->
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<!-- 追加 end -->
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
フォーム画面用のリンクの設定を追加します。
<blog/urls.py>
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_new, name='post_new'), <-- 追加
]
views.py
に新規追加用のメソッドpost_new()
を追加します。<blog/views.py>
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Post
from .forms import PostForm
from django.shortcuts import redirect
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
# 追加
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
*編集画面の追加
詳細画面から登録済みのデータを編集できるようにします。編集画面用のテンプレートを新規作成します。
<blog/template/blog_edit.html>
{% extends 'blog/base.html' %}
{% block content %}
<h1>New post</h1>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}
詳細画面に編集ボタンを追加します。
<blog/template/blog/post_detail.html>
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<!-- 追加 start -->
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
<!-- 追加 end -->
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endblock %}
編集用画面用のリンクの設定を追加します。
<blog/urls.py>
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_new, name='post_new'),
path('post/<int:pk>/edit/', views.post_edit, name='post_edit'), <-- 追加
]
データを更新するためのメソッド
post_edit()
を追加します。<blog/views.py>
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Post
from .forms import PostForm
from django.shortcuts import redirect
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
# 追加
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.publish_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
*ブログをアップロード
こちらはやらなくてもいいですが、PythonAnywhere
を使うとクラウド上のサーバーで作成したアプリケーションを公開することができます。https://www.pythonanywhere.com
bashエディタで下記コマンドを実行すると、ローカルでWebサーバーを起動したときと同様の画面を表示することができます。
$ pip3.6 install --user pythonanywhere
$ pa_autoconfigure_django.py https://github.com/<your-github-username>/my-first-blog.git
*所感
このチュートリアルをやったことで、Djangoの基本的な操作を学ぶことができました。わざとエラーにさせて、ログから原因を特定する方法や、Bootstrapの適用方法など書いてあるので、Webアプリケーション開発の初心者にとって非常にわかりやすい内容になっていて、学びが多かったです。今まで何となくしか理解できていなかったことも丁寧に書かれていました。PythonAnywhere
というサービスを初めて知りましたが、無料で使うことができて便利そうなので、今後機会がありましたら使ってみようと思います。Sign up here with your email
ConversionConversion EmoticonEmoticon