Object Serialization

Serialization in hx_requests ensures that Django model instances and other data types can be safely passed through HTMX requests. The serialization process takes place in the template tag.

Since HTMX requests don’t natively support passing complex Python objects, hx_requests provides a custom serialization method that allows Django models and additional data to be included in GET and POST requests.

Serializing Model Instances

When a model instance is serialized, it is converted into a structured string format that includes the prefix model_instance, to distinguish it from a plain JSON value, followed by the app label, model name, and primary key.

For example, a User instance with pk=5 in the auth app would be represented as:

model_instance__auth__user__5

This ensures that objects can be passed in requests without requiring JSON encoding.

If the value being serialized is not a Django model instance, it is simply converted to JSON.

Deserializing Model Instances

When a serialized object is received, it needs to be converted back into a Django model instance. This is done by extracting the app label, model name, and primary key, then retrieving the object from the database.

If the received value is not a model instance, it is treated as a standard JSON object.

This process ensures that Django objects can be reconstructed properly when handling HTMX requests.

Object scoping (get_queryset / model)

The instance is resolved through the model’s default manager — not _base_manager. Any scoping expressed on the default manager is therefore honored automatically: an object the default manager hides cannot be resolved.

For row-level authorization, override get_queryset() on the HxRequest (or set model), exactly as you would on a Django UpdateView. Resolution runs through that queryset, and a primary key outside it raises Http404 rather than silently loading another user’s row:

class EditInvoiceHx(FormModalHxRequest):
    name = "edit_invoice"

    def get_queryset(self):
        # A user can only resolve invoices they own.
        return Invoice.objects.filter(owner=self.request.user)

Because the round-trip is signed, the object reference cannot be forged; the get_queryset seam is what stops a replayed reference (a token minted for one user’s page, sent by another) from resolving out-of-scope data.

Handling kwargs Serialization

Each keyword argument is serialized by value and stored under its real name in the signed token’s kwargs dict — a dedicated sub-dict, not loose query parameters, so no name-mangling prefix is needed. When deserializing, each value is restored to its original form.

This allows hx_requests to safely pass complex parameters in HTMX requests.

Note

Kwargs live inside the signed token, not on the query string — so a kwarg is never read as a standard GET parameter. If you need to pass a real GET parameter through an HTMX request, use the hx-include or hx-vals attribute instead of passing it as a kwarg. For example, when implementing pagination, the page parameter must be sent as a standard GET parameter. Passing it as a kwarg will bury it in the token and it will be ignored by the request handler.

To read the kwargs back out of the token in your own view code, see Reading The Name, Object, And Kwargs.