Mixins
All mixins live in pragmatic.mixins.
Form Mixins
ReadOnlyFormMixin
Makes specific form fields read-only and disabled on existing instances.
from pragmatic.mixins import ReadOnlyFormMixin
from django import forms
class ProfileForm(ReadOnlyFormMixin, forms.ModelForm):
read_only = ('username', 'email')
class Meta:
model = User
fields = ['username', 'email', 'first_name']
Fields listed in read_only have readonly and disabled attributes
added to their widgets and are excluded from validation (they return the
current instance value through a custom clean_<field> method).
Set read_only_without_instance = True to enforce read-only even when
there is no instance.pk (useful for display-only forms that never have an
instance).
PickadayFormMixin
Fixes pickaday.js date/datetime inputs by adding a
data-value attribute in the format specified by settings.DATE_FORMAT.
from pragmatic.mixins import PickadayFormMixin
class EventForm(PickadayFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fix_fields(self, *args, **kwargs)
Access Control Mixins
LoginPermissionRequiredMixin
Extends Django’s PermissionRequiredMixin with raise_exception = True
and stores the missing permission on request.user.permission_error for use
in error handlers.
from pragmatic.mixins import LoginPermissionRequiredMixin
class InvoiceListView(LoginPermissionRequiredMixin, ListView):
permission_required = 'billing.view_invoice'
StaffRequiredMixin
Restricts a view to staff users (is_staff or is_superuser).
Unauthenticated or non-staff requests are redirected to
settings.LOGIN_REDIRECT_URL with a flash error message.
from pragmatic.mixins import StaffRequiredMixin
class AdminDashboard(StaffRequiredMixin, TemplateView):
template_name = 'admin/dashboard.html'
Set raise_exception = True on the class to raise PermissionDenied
instead of redirecting.
SuperuserRequiredMixin
Same as StaffRequiredMixin but restricts to superusers only.
Delete Mixins
DeleteObjectMixin
Handles object deletion in a DeleteView. Override get_success_url()
and optionally get_back_url() / get_failure_url().
from pragmatic.mixins import DeleteObjectMixin
from django.views.generic import DeleteView
class ArticleDeleteView(DeleteObjectMixin, DeleteView):
model = Article
success_url = reverse_lazy('article-list')
When PRAGMATIC_TRACK_DELETED_OBJECTS = True, a DeletedObject record is
created on successful deletion.
On ProtectedError (related objects block deletion), the view redirects to
get_failure_url() with a flash error.
Customisable attributes:
title— page title (default:'Delete object')message_success— flash message on successmessage_error— flash message onProtectedErrorback_url— URL to return to; falls back toobject.get_absolute_url()failure_url— URL on failure; falls back toback_url
CheckProtectedDeleteObjectMixin
Extends DeleteObjectMixin. Before showing the confirmation page, it
inspects NestedObjects and, if any protected relations exist, redirects
immediately with an error listing the blocking object types and counts.
from pragmatic.mixins import CheckProtectedDeleteObjectMixin
class CustomerDeleteView(CheckProtectedDeleteObjectMixin, DeleteView):
model = Customer
success_url = reverse_lazy('customer-list')
List View Mixins
SafePaginator
A Paginator subclass that clamps out-of-range page numbers to the last
page rather than raising EmptyPage. Also supports an optional
count_only_id=True argument to count using .only('id') for performance.
from pragmatic.mixins import SafePaginator
class ArticleListView(ListView):
paginator_class = SafePaginator
DisplayListViewMixin
Supports multiple display modes (e.g. list, table, map) controlled
by a ?display= query parameter. Automatically sets
template_name_suffix to _{display}, so a table display resolves
to myapp/article_table.html.
from pragmatic.mixins import DisplayListViewMixin
class ArticleListView(DisplayListViewMixin, ListView):
model = Article
displays = ['list', 'table']
paginate_by_display = {
'list': [10, 25, 50],
'table': 100,
}
The active display defaults to the first entry in displays. Per-display
pagination values are read from paginate_by_display; the first value in
the list is the default.
Context variables added: display_modes, paginate_by_display,
paginate_by.
SortingListViewMixin
URL-parameter-driven queryset sorting via ?sorting=<key>.
from pragmatic.mixins import SortingListViewMixin
class ArticleListView(SortingListViewMixin, ListView):
model = Article
sorting_options = {
'-created': 'Newest first',
'title': 'Title A–Z',
# tuple form: (display_label, queryset_ordering)
'-price': ('Price high–low', '-price'),
}
The active sorting defaults to the first key. Negative prefixes (-field)
use F(field).desc(nulls_last=True).
Context variable added: sorting_options.
PaginateListViewMixin
Minimal mixin that passes paginate_by from the view to the context. Use
this when you need get_paginate_by() override without the full
DisplayListViewMixin.
Model Mixins
SlugMixin
Auto-generates a URL-safe slug on save(), ensuring uniqueness by
appending an integer suffix if needed.
from pragmatic.mixins import SlugMixin
from django.db import models
class Article(SlugMixin, models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
# Optional overrides:
# SLUG_FIELD = 'title' # source field (default)
# MAX_SLUG_LENGTH = 150 # truncate before uniqueness check
# FORCE_SLUG_REGENERATION = True # regenerate on every save (default)
PDF Mixins
FPDFMixin
Helper for generating PDF responses using the fpdf2
library. Override write_pdf_content() to add content to self.pdf.
from pragmatic.mixins import FPDFMixin
from django.views.generic import View
class InvoicePdfView(FPDFMixin, View):
orientation = FPDFMixin.ORIENTATION_PORTRAIT
def get_filename(self):
return 'invoice.pdf'
def write_pdf_content(self):
self.pdf.set_font('Arial', size=12)
self.pdf.cell(200, 10, txt='Hello', ln=True)
def get(self, request, *args, **kwargs):
return self.render()
FPDFMixin constants:
FORMAT_A4/ORIENTATION_PORTRAIT/ORIENTATION_LANDSCAPEmargin_left,margin_right,margin_top,margin_bottom(default 8 mm each)
PdfDetailMixin
Renders an existing DetailView template as a PDF by POSTing the rendered
HTML to an external conversion API (HTMLTOPDF_API_URL or
PRINTMYWEB_URL).
from pragmatic.mixins import PdfDetailMixin
class InvoicePdfView(PdfDetailMixin, DetailView):
model = Invoice
template_name = 'billing/invoice_pdf.html'
inline = True # True = inline in browser, False = download
def get_filename(self):
return f'invoice-{self.get_object().number}.pdf'
Set HTMLTOPDF_API_URL or PRINTMYWEB_URL (plus PRINTMYWEB_TOKEN)
in your settings.