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()
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import DetailView
from .models import Invoice
class InvoiceDetailView(LoginRequiredMixin, DetailView):
model = Invoice
template_name = "invoices/invoice_detail.html"
context_object_name = "invoice"
def get_queryset(self):
# Scope the lookup set so foreign invoices resolve to a 404, not a 403.
return (
Invoice.objects
.for_user(self.request.user)
.select_related("owner")
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["is_overdue"] = self.object.is_overdue
return context
from datetime import date
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from .models import Invoice
User = get_user_model()
class InvoiceDetailViewTests(TestCase):
def setUp(self):
self.alice = User.objects.create_user("alice", password="pw")
self.bob = User.objects.create_user("bob", password="pw")
self.alice_invoice = Invoice.objects.create(
owner=self.alice,
number="INV-001",
amount_cents=5000,
due_on=date(2030, 1, 1),
)
def url(self):
return reverse("invoices:detail", args=[self.alice_invoice.pk])
def test_can_view_own_invoice(self):
self.client.force_login(self.alice)
response = self.client.get(self.url())
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["invoice"], self.alice_invoice)
def test_cannot_view_other_users_invoice(self):
self.client.force_login(self.bob)
response = self.client.get(self.url())
self.assertEqual(response.status_code, 404)
def test_anonymous_is_redirected_to_login(self):
response = self.client.get(self.url())
self.assertEqual(response.status_code, 302)
self.assertIn("/login", response["Location"])
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
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
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
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
Django URL namespacing and reverse lookups
#!/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
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)
import graphene
from graphene_django import DjangoObjectType
from blog.models import Post, Comment
class PostType(DjangoObjectType):
Django GraphQL with Graphene
Share this code
Here's the card — post it anywhere.