How To Scope The hx_object Queryset
When an HxRequest carries an object, the round-trip reference is resolved
through get_queryset. By default that queryset is the object’s model
default manager, so any scoping expressed there (soft-delete filters, tenant
boundaries) is honored automatically, and a primary key outside the queryset
raises Http404 instead of loading the row.
That default is deliberately narrow. This guide shows how to resolve the object through a different queryset — a custom manager, a broader lookup, or a per-request scope.
For why resolution is scoped and how it relates to signing, see Object Serialization.
Set model
The simplest override. Point the handler at a model and resolution runs through
that model’s default manager, mirroring Django’s SingleObjectMixin:
from hx_requests.hx_requests import BaseHxRequest
class EditInvoiceHx(BaseHxRequest):
name = "edit_invoice"
model = Invoice
Override get_queryset
For anything beyond the default manager, override get_queryset and return
the queryset you want the object resolved through. Whatever you return is the
authoritative lookup — a pk outside it is a Http404.
Resolve through a custom manager. Use a manager other than the default — for example one that widens the lookup back to rows the default manager hides:
class RestoreInvoiceHx(BaseHxRequest):
name = "restore_invoice"
def get_queryset(self):
# Include soft-deleted rows the default manager filters out.
return Invoice.all_objects.all()
Broaden for privileged users.
get_queryset runs per request, so you can widen or narrow the scope based
on who is asking:
class EditInvoiceHx(BaseHxRequest):
name = "edit_invoice"
def get_queryset(self):
if self.request.user.is_staff:
return Invoice.objects.all()
return Invoice.objects.filter(owner=self.request.user)
Note
Returning None from get_queryset (the default when no
model is set) falls back to the resolved object’s own model default
manager. Return an explicit queryset only when you need a scope other than
that.
Warning
Widening the queryset widens what a replayed reference can resolve. A token
minted for one user’s page can be sent by another; get_queryset is
the boundary that keeps resolution in scope. Only broaden it — for example
with all_objects or an unfiltered objects.all() — where the
handler’s own checks make that safe.