Mixin hợp khi có một behaviour lặp ở nhiều CBV không liên quan model — auth check, scope theo tenant, gắn extra context.
Mixin phải kế thừa từ object (không phải View), override một method ngắn, và gọi super() đúng cách.
class TenantScopedMixin:
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(tenant=self.request.tenant)
class AuditedDeleteMixin:
def form_valid(self, form):
response = super().form_valid(form)
AuditLog.objects.create(actor=self.request.user, action='delete', target=self.object)
return response
class PostDeleteView(LoginRequiredMixin, TenantScopedMixin, AuditedDeleteMixin, DeleteView):
model = PostThứ tự kế thừa quan trọng: mixin đứng trước, base view đứng sau. Python MRO duyệt trái → phải, nên LoginRequiredMixin phải ở đầu để chặn sớm trong dispatch.
Bẫy hay gặp khi mixin chồng nhau: 2 mixin cùng override một method mà không gọi super() thì một cái sẽ "ăn" mất cái còn lại, gây bug rất khó tìm. Khi MRO rối, in ClassName.__mro__ ra xem trật tự thực. Thường thì đây cũng là dấu hiệu nên gom logic vào service layer thay vì nhồi thêm mixin.
A Mixin is right when a behaviour (auth check, tenant scoping, extra context) repeats across unrelated CBVs.
The Mixin must inherit from object (not View), override one short method, and call super() correctly.
class TenantScopedMixin:
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(tenant=self.request.tenant)
class AuditedDeleteMixin:
def form_valid(self, form):
response = super().form_valid(form)
AuditLog.objects.create(actor=self.request.user, action='delete', target=self.object)
return response
class PostDeleteView(LoginRequiredMixin, TenantScopedMixin, AuditedDeleteMixin, DeleteView):
model = PostInheritance order: mixins first, base view last. Python MRO walks left → right, so LoginRequiredMixin must be at the front to short-circuit early in dispatch.
Pitfall: Avoid mixins that override the same method in opposite directions (both rewrite get_queryset without calling super()) — one quietly swallows the other. When the MRO gets confusing, print ClassName.__mro__ to debug; it usually means logic belongs in a service rather than another mixin.