Common Mistakes
Learn from common pitfalls when building with djust. This guide covers the most frequent mistakes developers make and how to avoid them.
💡 Tip: Run python manage.py check to catch many of these issues early. djust includes built-in system checks that warn about missing **kwargs, unsafe mark_safe usage, and more.
1. Using Server Handlers for Client-Side Navigation
❌ Problem
Attempting to navigate using event handlers instead of template directives
Why This Happens
Client-side navigation (dj-navigate, dj-patch) is meant to be declarative in templates. Using server-side handlers for simple link navigation adds unnecessary round-trips and breaks browser behavior like middle-click to open in new tab.
Wrong Approach
# views.py - WRONG
@event_handler
def go_to_item(self, item_id: int = 0, **kwargs):
self.live_redirect(f'/items/{item_id}/')
# template - WRONG
<button dj-click="go_to_item" data-item-id="{{ item.id }}">
View Item
</button>
Correct Approach
# template - CORRECT
<a dj-navigate="/items/{{ item.id }}/">View Item</a>
# Or for query params only:
<a dj-patch="?filter={{ filter }}&sort=name">Apply Filter</a>
✅ Solution
Use dj-navigate for full page navigation and dj-patch for URL param updates. Only use server handlers when navigation logic requires server-side authorization, data fetching, or conditional redirect logic.
2. Calling live_navigate() (Does Not Exist)
❌ Problem
Attempting to call self.live_navigate() instead of self.live_redirect()
Why This Happens
The method is named live_redirect(), not live_navigate(). This is a common typo because the template directive is named dj-navigate, but the server method uses redirect to align with Django's redirect() pattern.
Wrong Approach
@event_handler
def save_item(self, **kwargs):
item = Item.objects.create(...)
# WRONG - AttributeError: 'MyView' object has no attribute 'live_navigate'
self.live_navigate(f'/items/{item.id}/')
Correct Approach
@event_handler
def save_item(self, **kwargs):
item = Item.objects.create(...)
# CORRECT
self.live_redirect(f'/items/{item.id}/')
✅ Solution
Use self.live_redirect(path) for server-side navigation. Use self.live_patch(params=...) to update URL params without remounting.
3. No dj-root (or dj-view) on the Template Root
❌ Problem
The outermost element carries neither dj-root nor dj-view
Why This Happens
djust needs one attribute on the root element to know where the reactive region begins. Given either one it fills in the other: with dj-root the server stamps dj-view with the dotted path of the view rendering the page, and with dj-view the client stamps dj-root. With neither, no dj-view reaches the browser, no WebSocket is established, and the page is silently static — no error, it just never updates.
Wrong Approach
<!-- templates/counter.html - WRONG (neither attribute) -->
<div>
<h1>Count: {{ count }}</h1>
<button dj-click="increment">+1</button>
</div>
Correct Approach
<!-- templates/counter.html - CORRECT -->
<div dj-root>
<h1>Count: {{ count }}</h1>
<button dj-click="increment">+1</button>
</div>
✅ Solution
Put dj-root on the outermost element. djust fills in dj-view for you with the view that rendered the page, so you do not repeat the dotted path in the template. Write dj-view="myapp.views.CounterView" explicitly only when you need to name a specific view — an embedded or sticky view, or a template shared by several views.
4. Storing Non-Serializable Objects in View State
❌ Problem
Storing service clients, database connections, or other non-serializable objects as instance attributes
Why This Happens
djust serializes all view state for WebSocket transport between renders. Objects like boto3.client, httpx.Client, requests.Session, file handles, or database cursors are not JSON-serializable and cause runtime errors.
Wrong Approach
# views.py - WRONG
def mount(self, request, **kwargs):
self.s3_client = boto3.client('s3') # Will fail on serialize!
@event_handler
def upload_file(self, **kwargs):
self.s3_client.upload_file(...) # Error before this even runs
Correct Approach
# views.py - CORRECT
def _get_s3_client(self):
"""Helper method to create client on demand."""
return boto3.client('s3')
@event_handler
def upload_file(self, **kwargs):
client = self._get_s3_client() # Created fresh each call
client.upload_file(...)
✅ Solution
Use helper methods that create service instances on demand. Prefix them with underscore (_get_client) to keep them private. Never store clients, connections, or non-serializable objects as self.attribute.
5. Missing **kwargs on Event Handlers
❌ Problem
Event handler signatures without **kwargs parameter
Why This Happens
The client sends additional context (data-* attributes, form fields, metadata) as keyword arguments. Without **kwargs, Python raises TypeError when unexpected params arrive, even if you only care about specific parameters.
Wrong Approach
@event_handler
def delete_item(self, item_id: int = 0): # Missing **kwargs!
Item.objects.filter(id=item_id).delete()
# Template sends extra params:
<button dj-click="delete_item"
data-item-id="{{ item.id }}"
data-confirm="true"> <!-- Extra param = TypeError -->
Delete
</button>
Correct Approach
@event_handler
def delete_item(self, item_id: int = 0, **kwargs): # ✓ Accepts extra params
Item.objects.filter(id=item_id).delete()
# Now this works fine:
<button dj-click="delete_item"
data-item-id="{{ item.id }}"
data-confirm="true">
Delete
</button>
✅ Solution
Always include **kwargs in every event handler signature. This is a hard requirement. The system check djust.V004 will warn about handlers missing **kwargs.
Additional Tips
Use System Checks
Run python manage.py check to catch common mistakes early. djust includes checks for missing **kwargs, unsafe mark_safe usage, and more.
python manage.py check
Enable Debug Mode During Development
Set DEBUG=True and enable VDOM debugging to see what DOM operations are happening.
# settings.py
DEBUG = True
LIVEVIEW_CONFIG = {
'debug_vdom': True, # See VDOM patches in browser console
}
Check Template Requirements
Every LiveView template needs dj-root (or an explicit dj-view) on its outermost element — djust fills in the other.
<div dj-root>
<!-- your content -->
</div>
Related Resources
- Getting Started - Learn the basics right
- Core Concepts - Understand how djust works
- API Reference - Complete API documentation
- Error Reference - Detailed error code explanations