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.