Django ビューとURL設計
関数ビュー(FBV)
# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse
from .models import Post
def post_list(request):
posts = Post.objects.filter(status='published')
return render(request, 'blog/post_list.html', {
'posts': posts,
})
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug, status='published')
return render(request, 'blog/post_detail.html', {
'post': post,
})
def post_create(request):
if request.method == 'POST':
title = request.POST.get('title')
body = request.POST.get('body')
post = Post.objects.create(
title=title, body=body,
author=request.user, status='draft'
)
return redirect('blog:post_detail', slug=post.slug)
return render(request, 'blog/post_form.html')
URL設計
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('create/', views.post_create, name='post_create'),
path('<slug:slug>/', views.post_detail, name='post_detail'),
]
# config/urls.py
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
クラスビュー(CBV)
from django.views.generic import (
ListView, DetailView, CreateView, UpdateView, DeleteView
)
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10
def get_queryset(self):
return Post.objects.filter(status='published')
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
slug_field = 'slug'
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'body', 'category', 'status']
template_name = 'blog/post_form.html'
success_url = reverse_lazy('blog:post_list')
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UpdateView):
model = Post
fields = ['title', 'body', 'category', 'status']
template_name = 'blog/post_form.html'
class PostDeleteView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('blog:post_list')
URLパスコンバータ
urlpatterns = [
path('post/<int:pk>/', views.post_detail), # 整数
path('post/<slug:slug>/', views.post_detail), # スラグ
path('post/<str:title>/', views.post_detail), # 文字列
path('archive/<int:year>/<int:month>/', views.archive),
]
JSONレスポンス(API)
from django.http import JsonResponse
def api_posts(request):
posts = Post.objects.filter(status='published').values(
'id', 'title', 'slug', 'created_at'
)
return JsonResponse(list(posts), safe=False)
デコレータによるアクセス制御
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
@login_required
def my_posts(request):
posts = request.user.posts.all()
return render(request, 'blog/my_posts.html', {'posts': posts})
@require_POST
def post_delete(request, pk):
post = get_object_or_404(Post, pk=pk, author=request.user)
post.delete()
return redirect('blog:post_list')
まとめ
- 関数ビューはシンプル、クラスビューは再利用性が高い
path() でURLパターンを定義、include() でアプリごとに分割
app_name で名前空間を設定し reverse('blog:post_list') で逆引き
LoginRequiredMixin / @login_required でアクセス制御
get_object_or_404 で存在しないオブジェクトを404エラーに