Menu
CoddyTech

Only the Poster Edits

Part of the Class-Based Views section of Coddy's Django journey. Lesson 16 of 18.

Log in as grace and open /jobs/python-developer-brightline/edit/: ada posted that job, and grace can change it. Job pages show Edit only to the poster, but anyone can type the address or send a POST, so the view itself must refuse.

UserPassesTestMixin calls a method you write, test_func(), before the view runs. True lets the request through; False answers 403 Forbidden to a logged-in user, on GET and POST alike. A recipe site lets nobody change a published recipe:

class RecipeUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Recipe
    fields = ["title", "minutes"]

    def test_func(self):
        return not self.get_object().published

The test runs before the view has looked the row up, so self.object does not exist yet. self.get_object() finds the row from the URL, the way the view will.

challenge icon

Challenge

Medium

Only a job's poster may edit or delete it:

  • Add UserPassesTestMixin to JobUpdateView and JobDeleteView, after LoginRequiredMixin.
  • The test passes only for the user who posted the job. Anyone else gets 403 Forbidden, on GET and POST.

Press Run, log in as grace and open the edit page above.

Try it yourself

from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import CreateView, DeleteView, DetailView, ListView, TemplateView, UpdateView

from .models import Job


class HomeView(TemplateView):
    template_name = "jobs/home.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["count"] = Job.objects.count()
        context["latest"] = Job.objects.order_by("-posted").first()
        return context


class AboutView(TemplateView):
    template_name = "jobs/about.html"


class JobListView(ListView):
    model = Job
    context_object_name = "jobs"
    ordering = ["-posted"]
    paginate_by = 5

    def get_queryset(self):
        jobs = super().get_queryset()
        city = self.request.GET.get("city")
        if city:
            jobs = jobs.filter(city=city)
        return jobs


class JobDetailView(DetailView):
    model = Job


class JobCreateView(LoginRequiredMixin, CreateView):
    model = Job
    fields = ["title", "company", "city", "salary", "description"]

    def form_valid(self, form):
        form.instance.posted_by = self.request.user
        return super().form_valid(form)


class JobUpdateView(LoginRequiredMixin, UpdateView):
    model = Job
    fields = ["title", "company", "city", "salary", "description"]


class JobDeleteView(LoginRequiredMixin, DeleteView):
    model = Job
    success_url = reverse_lazy("jobs:list")
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Class-Based Views

Practice on your own: Django playground