記事一覧へ戻る 本の順番で続きを読む

Django ビューとURL設計 - リクエスト処理の基本

Python3上級 | 2026/02/18 21:20

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エラーに