はじめに
年内に0.0.1バージョンくらいまでは完成させてサービス告知の記事を執筆しようと思うのでペースあげます。
コーディングスタイルを統一させる
そろそろ本格的にコーディングする事になるので、ここいらでPythonのコーディング規約に基づくコーディングができるようにしておきます。
PythonではPEPというドキュメント群があり、Pythonコミュニティに対して情報を提供するドキュメントがあります。その中でPythonのコードスタイルについて書かれているのがPEP8
となります。
https://pep8-ja.readthedocs.io/ja/latest/
さて、このドキュメントを一読した後はソースコードをチェックできるツールを探してみましょう。
pycodestyleでソースコードチェック
https://pypi.org/project/pep8/
PythonにはPEP8パッケージが存在しますが、パッケージの方は後方互換のために残されている模様でこちらを実行するとWARNINGが発生しました。
% pip install pep8
% pep8
pep8 has been renamed to pycodestyle (GitHub issue #466)
Use of the pep8 tool will be removed in a future release.
Please install and use `pycodestyle` instead.
https://pypi.org/project/pycodestyle/
新しく、pycodestyleという名前のパッケージがリリースされているので、こちらでソースコードのチェックを行なってみます。
% pip install pycodestyle
% find ./ -name "*.py" -print | xargs pycodestyle
./manage.py:6:80: E501 line too long (83 > 79 characters)
警告が出たらその都度直していきましょう。
この他にも、pyflakes
というそもそもPythonの文法自体のエラーを検出してくれるツールも便利ですのでこちらも導入します。
【Django】DjangoによるWeb開発プロジェクトPart2 - ログインページを作成する所まで -
% pip install pyflakes
% find ./ -name "*.py" -print | xargs pyflakes
UserモデルとのRelationShipを設定する
コーディングスタイルを設定したので、いよいよ本格的にコードを書いていきます。
Djangoでは、アプリケーションを構築してコードを書いていきます。アプリケーションの考え方についてはこちら→https://qiita.com/himrock922/items/bd020a0d313233556c97
今回、プライベートリポジトリで開発を進めているので、アプリケーション名は多少ぼかしています。ご了承下さい。
from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url
from django.views.generic.base import TemplateView
urlpatterns = [
path('django_admins/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('', TemplateView.as_view(template_name='index.html')),
url(r'^polls/', include('polls.urls')),
]
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
ここで作ったpollsアプリケーションは、デフォルトのauthアプリケーションで作成されたUserモデルと関連付けをさせる予定です。
当初、Userをカスタマイズする予定がなかったため、デフォルトの設定をそのまま使用していましたが、例えauthアプリケーションをそのまま使う予定だとしても、カスタマイズしたUserモデルを使うことが推奨されています。
https://docs.djangoproject.com/ja/2.1/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project
という事で、一旦migrationをリセットして、Userモデルをオーバーライドします。
% python manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
% python manage.py migrate --fake auth zero
Operations to perform:
Unapply all migrations: auth
Running migrations:
Rendering model states... DONE
Unapplying auth.0009_alter_user_last_name_max_length... FAKED
Unapplying auth.0008_alter_user_username_max_length... FAKED
Unapplying auth.0007_alter_validators_add_error_messages... FAKED
Unapplying auth.0006_require_contenttypes_0002... FAKED
Unapplying auth.0005_alter_user_last_login_null... FAKED
Unapplying auth.0004_alter_user_username_opts... FAKED
Unapplying auth.0003_alter_user_email_max_length... FAKED
Unapplying auth.0002_alter_permission_name_max_length... FAKED
Unapplying admin.0003_logentry_add_action_flag_choices... FAKED
Unapplying admin.0002_logentry_remove_auto_add... FAKED
Unapplying admin.0001_initial... FAKED
Unapplying auth.0001_initial... FAKED
% python manage.py showmigrations
admin
[ ] 0001_initial
[ ] 0002_logentry_remove_auto_add
[ ] 0003_logentry_add_action_flag_choices
auth
[ ] 0001_initial
[ ] 0002_alter_permission_name_max_length
[ ] 0003_alter_user_email_max_length
[ ] 0004_alter_user_username_opts
[ ] 0005_alter_user_last_login_null
[ ] 0006_require_contenttypes_0002
[ ] 0007_alter_validators_add_error_messages
[ ] 0008_alter_user_username_max_length
[ ] 0009_alter_user_last_name_max_length
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
sessions
[X] 0001_initial
usersアプリケーションを作成して、authのクラスをオーバーライドします。
% python manage.py startapp users
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'webpack_loader',
'users',
]
AUTH_USER_MODEL = 'users.User'
DB再migration
% sudo su - postgres
% psql
postgres=# DROP DATABASE application_development;
DROP DATABASE
postgres=# CREATE DATABASE application_development;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE application_development TO application_development;
前述で特定の箇所のmigrationをROLLBACKしていましたが、一度DBをDropしました。
再度migrationすることにします。なお、この時にusersアプリケーションのinitalizeのmigrationファイルを作成したいので、usersアプリケーションのmigrationファイルを作成しています。
% python manage.py makemigration users
% python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, users
Running migrations:
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0001_initial... 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 users.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 sessions.0001_initial... OK
なお、Userモデルのオーバーライドについてこんな記事もありました。
Djangoでは常にカスタムUserを使用すべき
今後、どうなるか分かりませんが、とりあえずカスタムUserモデルを作って柔軟に対応できるようにしようという問題は解決したので、僕の方では一旦AbstractUserモデルをオーバーライドするようにしています。
と、ここまで設定したのですが、現在開発しているアプリケーションで利用しているライブラリがデータを標準出力で返してくれているので、一先ずRelationShipは置いておいて、index,detailページへの展開出力を行なっていきます。
views.pyのclassでtemplateに変数を持たせる
まずは、indexとdetailページを作ってあげます。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<int:****_id>/', views.detail, name='detail'),
]
また、各views.pyの役割としてHTTPレスポンスを正常に返す場合の処理と例外処理を行う場合の2通りの役割があります。この事も考慮してviews.pyの中をコーディングしていきます。
実際にtemplateに変数を渡す際には
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
のように、polls/index.html
templateをロードし、コンテキストを渡してあげます。コンテキストは、テンプレート変数名をPythonオブジェクトに渡す辞書のようなものです。
Djangoには上記の処理をもう少し、モダンにかけるようにrender()
メソッドが用意されています。
from django.shortcuts import render
from django.http import Http404
from ***** import *****
def index(request):
try:
**** = *****
except (Exception, SystemExit):
raise Http404("**** does not exist")
return render(request, '****/index.html', {'****': ****.****})
def detail(request, ***_name)
try:
test = *****.****(****_name)
except (Exception, SystemExit):
raise Http404("**** does not exist")
return render(request, '****/detail.html', {'****': *****})
色々とぼかしてすみません。例外処理ももう少し丁寧に後々、書いていきます。
ひとまず、Http404
という例外をDjangoで提供しているため、これでキャッチしておきます。
index.htmlの方でcontextを展開してリスト表示してみます。
{% extends 'application.html' %}
{% block title %}**** List{% endblock %}
{% block content %}
<main class="dashboard">
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a href="{% url '****:index' %}" class="navbar-brand col-sm-3 col-md-2 mr-0">Jails</a>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="{% url 'logout' %}">log out</a>
</li>
</ul>
</nav>
<div class="container-fluid">
<h2>****s</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>***</th>
<th>***</th>
<th>***</th>
<th>***</th>
<th>***</th>
</tr>
</thead>
<tbody>
{% for *** in **** %}
<tr>
<td>{{ ***.0 }}</td>
<td>{{ ***.1 }}</td>
<td>{{ ***.2 }}</td>
<td>{{ ***.3 }}</td>
<td>{{ ***.4 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</main>
{% endblock %}
ちなみに、Djangoの記述で配列を展開していく場合は[0]
ではなく、.0
と書くのが正しい模様です。
https://stackoverflow.com/questions/19895894/could-not-parse-the-remainder-0-from-item0-django
次にdetailページを作るわけですが、今回使っているライブラリがidではなく、名前で設定を表示するようになっていましたので、urls.pyで文字列をパラメータとして渡せるように書き換えます。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<str:****_name>/', views.detail, name='detail'),
]
また、detailの詳細情報は配列ではなく、ハッシュ形式なため、ハッシュを展開できるようにします。ややこしいですね。
(中略)
<tbody>
{% for k, v in ****.items %}
<tr>
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
{% endfor %}
</tbody>
まとめ
色々とぼかしまくってすみません汗
冒頭にも述べた通り、年内に0.0.1バージョンくらいまでは完成させてサービス告知の記事を執筆しようと思うのでペースあげます。