【Django】すべてをviews.pyのみに書いていたのをdomain.pyを作ってリファクタリングした
DB関連の処理も、計算関連の処理のための関数も、全てviewsに書いていましたが、
viewsに全てを書くのをやめました!
という話です。
動くからいいやん!と思っていましたが、viewsにはviewsの役割があり、上記の処理はviewsでやることではないとか。
半年ほど前に下書きのまま放置していたのをようやく投稿(^^;
viewsやmodeslなど、それぞれどんな役割か?
ざっくり、forms・models・viewsの役割はこんな感じ。
- forms
フォームの入力項目やバリデーションを管理します。
- models
データベースに格納するデータを管理します。
テーブルとかカラムとか、データベース設計はmodels.pyを見れば把握できます。
- views
リクエストやリスポンスにまつわる処理を管理します。
送られてきたリクエストをもとに、どのような内容を表示させるかを決定をしています。
CodeBeforeAfter
ユーザー登録機能を例にあげます。
過去記事のです(過去の未熟さを積極的にさらすスタイルでいきます笑)
まず、forms、models、viewsだけのコードがこちら↓
forms.py
from django import forms from django import forms class RegistrationForm(forms.Form): username = forms.CharField() password = forms.CharField() email = forms.EmailField()
models.pyは、ユーザー登録に関してはUserクラスをインポートすればmodelsに書かなくても良いので割愛。
views.py
import django.http import myApp.forms from django.shortcuts import render import uuid from django.contrib.auth.models import User import re def has_digit(text): if re.search("\d", text): return True return False def has_alphabet(text): if re.search("[a-zA-Z]", text): return True return False def registation_user(request): if request.method == 'POST': registration_form = myApp.forms.RegistrationForm(request.POST) password = request.POST['password'] if len(password) < 8: registration_form.add_error('password', "文字数が8文字未満です。") if not has_digit(password): registration_form.add_error('password',"数字が含まれていません") if not has_alphabet(password): registration_form.add_error('password',"アルファベットが含まれていません") if registration_form.has_error('password'): return render(request, 'registration.html', {'registration_form': registration_form}) user = User.objects.create_user(username=request.POST['username'], password=password, email=request.POST['email']) return django.http.HttpResponseRedirect('/login') else: registration_form = myApp.forms.RegistrationForm() return render(request, 'registration.html', {'registration_form': registration_form})
そのままでも動きますが、
viewsに登録や判定など
表示に関係ない部分もまとめて書いています。
コードが長くなると、どこでどの処理を行っているのかを把握するのが困難になります。
また、viewsが長すぎて悪い部分を修正するのが大変になります。
そこで、domains.pyを作成し、
viewにはリクエストをもとに表示内容を決める処理を書き、
domainsは計算などのそのシステムの中核となる処理を書くようにします。
役割を分けて書いた結果(viewsとdomains以外は変わっていないのでforms.pyは割愛)↓
domains.py
def post_new_post(author: User, post: Dict[str, Any], file: MultiValueDict) -> None: if not author.is_authenticated: raise minsta.exceptions.LoginRequiredError() form = minsta.forms.NewPostForm(post, file) if not form.is_valid(): raise minsta.exceptions.ValidationError(form) file_path = handle_uploaded_file(form.cleaned_data['file']) print(type(file)) minsta.models.Post.create(author, file_path, form.cleaned_data['comment']) def handle_uploaded_file(f): file_path = "uploads/{}.jpeg".format(uuid.uuid4().hex) with open('minsta/static/{}'.format(file_path), 'wb') as destination: # 'minsta/static/uploads/{}.jpeg'.format(uuid.uuid4().hex) .format()で{}の中を()の中に置き換えてくれる # uuidとは、ランダムでユニークなIDの規格である。uuid.uuid4().hexの.hexで文字列にする。 for chunk in f.chunks(): destination.write(chunk) return file_path
views.py
import django.http import minsta.models import minsta.forms from django.shortcuts import render from django.contrib.auth.models import User import minsta.domain def get_post_new_post(request): try: minsta.domain.check_authenticated(request.user) except minsta.exceptions.LoginRequiredError: return django.http.HttpResponseRedirect('/login') form = minsta.forms.NewPostForm() return render(request, 'new_post.html', {'form': form, 'user':request.user}) def post_post_new_post(request): try: minsta.domain.post_new_post(request.user, request.POST, request.FILES) except minsta.exceptions.LoginRequiredError: return django.http.HttpResponseRedirect('/login') except minsta.exceptions.ValidationError as e: return render(request, 'new_post.html', {'form': e.form, 'user':request.user}) return django.http.HttpResponseRedirect('/list') def post_new_post(request): if request.method == 'POST': return post_post_new_post(request) return get_post_new_post(request)
viewsとdomainsを分けると、
修正が必要になった際に
viewsとdomainsのどちらが悪いのかが分かり
悪い方だけ修正すれば良くなるので
修正範囲が小さくなり、修正がより楽になるのでおすすめです。