python 99 lines · 3 tabs

Scope a Django DetailView to the Request User with get_queryset

Shared by codesnips Sep 2026
3 tabs
from django.conf import settings
from django.db import models
from django.utils import timezone


class InvoiceQuerySet(models.QuerySet):
    def for_user(self, user):
        return self.filter(owner=user)

    def overdue(self):
        return self.filter(paid_at__isnull=True, due_on__lt=timezone.localdate())


class Invoice(models.Model):
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="invoices",
    )
    number = models.CharField(max_length=32, unique=True)
    amount_cents = models.PositiveIntegerField()
    due_on = models.DateField()
    paid_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    objects = InvoiceQuerySet.as_manager()

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.number

    @property
    def is_overdue(self):
        return self.paid_at is None and self.due_on < timezone.localdate()
3 files · python Explain with highlit

A common access-control bug in Django apps is the Insecure Direct Object Reference (IDOR): a DetailView looks up an object purely by its primary key from the URL, so any authenticated user can view anyone else's record just by changing the number in the URL. The fix is to scope the queryset the view searches in, rather than filtering after the fact, so that objects belonging to other users simply do not exist as far as this view is concerned.

In models.py, Invoice carries an owner foreign key to the user, and the custom InvoiceQuerySet.for_user method encapsulates the ownership rule in one place on the ORM. Exposing this as a manager method (Invoice.objects.for_user(...)) means the same scoping can be reused across views, serializers, and shell scripts, and the authorization logic lives with the data instead of being scattered through view code.

In views.py, InvoiceDetailView overrides get_queryset to return Invoice.objects.for_user(self.request.user). This is the key idea: DetailView.get_object calls get_queryset and then applies the pk/slug lookup against that already-narrowed set. Because the base queryset only contains the current user's invoices, requesting someone else's pk raises Http404 via get_object_or_404 internally — the user is told the record does not exist rather than that it exists but is forbidden, which avoids leaking information. LoginRequiredMixin guarantees self.request.user is a real authenticated user before any of this runs.

Overriding get_queryset is preferred over overriding get_object because it composes: pagination, filtering, and select_related all continue to work, and the same pattern applies unchanged to a ListView. The select_related('owner') call avoids an extra query when the template renders owner details. get_context_data is shown adding a derived is_overdue flag so the view stays the single source of truth.

The test_views.py tab locks the behavior down: test_cannot_view_other_users_invoice asserts a 404 when a user requests a stranger's invoice, and test_can_view_own_invoice asserts a 200. These two tests are what turn a convention into an enforced guarantee, catching regressions if someone later swaps in a broader queryset.


Related snips

ruby
payload = {
  sub: user.id,
  iss: 'https://auth.example.com',
  aud: 'codesnips-api',
  exp: 15.minutes.from_now.to_i,
  iat: Time.now.to_i,

JWT issuance and verification without common footguns

jwt authentication api
by Kai Nakamura 2 tabs
python
class Product(models.Model):
    name = models.CharField(max_length=200)
    slug = models.SlugField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    cost = models.DecimalField(max_digits=10, decimal_places=2)
    margin = models.DecimalField(max_digits=5, decimal_places=2, blank=True)

Django model signals vs overriding save

django python models
by Priya Sharma 2 tabs
python
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [

Django URL namespacing and reverse lookups

django python urls
by Priya Sharma 3 tabs
bash
#!/usr/bin/env bash
set -euo pipefail

export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"

Secrets management with environment isolation and Vault

secrets-management vault environment-variables
by Kai Nakamura 1 tab
typescript
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";

const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";

JWT access + refresh token rotation (conceptual)

security node jwt
by codesnips 3 tabs
python
import graphene
from graphene_django import DjangoObjectType
from blog.models import Post, Comment


class PostType(DjangoObjectType):

Django GraphQL with Graphene

django python graphql
by Priya Sharma 2 tabs

Share this code

Here's the card — post it anywhere.

Scope a Django DetailView to the Request User with get_queryset — share card
Link copied