Customisation tips
Once the basics are in place you may want to tailor templates, extend mixins, or integrate PowerCRUD into broader workflows. This chapter rounds up the common tweaks and points to deeper references.
1. Copy and tweak templates
Use the management command to copy PowerCRUD templates into your app:
# Copy entire structure
python manage.py pcrud_mktemplate myapp --all
# Just the CRUD templates for a model
python manage.py pcrud_mktemplate myapp.Project --all
# Specific pieces
python manage.py pcrud_mktemplate myapp.Project --list
python manage.py pcrud_mktemplate myapp.Project --form
Templates land in myapp/templates/myapp/… mirroring PowerCRUD’s layout. Override only what you need; remove a copied file to fall back to the default.
2. Extend mixins and views
Because PowerCRUD leans on mixins, you can override behaviour by subclassing:
class ProjectCRUDView(PowerCRUDMixin, CRUDView):
# …
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(owner=self.request.user)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
Useful hooks:
get_queryset,get_filter_queryset_for_field.get_bulk_choices_for_field,get_bulk_selection_key_suffix.persist_single_objectfor validated standard form and inline writes.persist_bulk_updatefor synchronous bulk update writes.get_context_datafor injecting extra template data.
If your sync bulk service needs to reject the batch with a user-facing validation message, return the standard handled bulk result payload instead of raising an unhandled exception. See Bulk editing (synchronous).
Use the Hooks reference for the canonical hook contracts and signatures.
If you want a more guided explanation of when persistence hooks are worth using, start with the Advanced Guides, especially Persistence Hooks for Real Write Logic and Async Bulk Persistence Without Surprises.
Persistence hooks
Use the sync persistence hooks when your app needs validated PowerCRUD inputs but wants business write orchestration to live in an app service rather than in several separate view overrides.
class ProjectCRUDView(PowerCRUDMixin, CRUDView):
def persist_single_object(self, *, form, mode, instance=None):
project = super().persist_single_object(
form=form,
mode=mode,
instance=instance,
)
ProjectAuditService().record_save(project=project, mode=mode)
return project
def persist_bulk_update(
self,
*,
queryset,
fields_to_update,
field_data,
progress_callback=None,
):
return ProjectBulkUpdateService().apply(
queryset=queryset,
fields_to_update=fields_to_update,
field_data=field_data,
)
Notes:
persist_single_object(...)is used by both the normal form save path and inline row save path.modeis currently"form"or"inline".- If you bypass
form.save()inpersist_single_object(...), your override owns any requiredform.save_m2m()handling. persist_bulk_update(...)is sync bulk-update only in this release. Bulk delete and async bulk persistence remain separate follow-up concerns.- The full hook contract now lives in the Hooks reference.
Migrating older write overrides
If an existing downstream project is overriding internal methods only to take control of persistence, move that logic onto the public hooks instead:
- move standard form-save write logic from
form_valid()intopersist_single_object(...) - move inline write logic from inline-save internals into the same
persist_single_object(...) - move sync bulk-update write logic from bulk internals into
persist_bulk_update(...) - move async bulk-update write logic out of worker patches and into
BulkUpdatePersistenceBackendplusbulk_update_persistence_backend_path
Keep the older override only when it also changes validation, response handling, or another non-persistence part of the flow.
3. Custom buttons & actions
class ProjectCRUDView(PowerCRUDMixin, CRUDView):
extra_buttons = [
{
"url_name": "projects:new",
"text": "New project",
"button_class": "btn-primary",
"display_modal": True,
},
{
"url_name": "projects:selected-summary",
"text": "Selected summary",
"display_modal": True,
"uses_selection": True,
"selection_min_count": 1,
"selection_min_behavior": "disable",
},
]
extra_actions = [
{
"url_name": "projects:clone",
"text": "Clone",
"needs_pk": True,
"htmx_target": "#powercrudModalContent",
"display_modal": True,
"disabled_if": "is_clone_disabled",
"disabled_reason": "get_clone_disabled_reason",
},
]
Button dictionaries support additional keys such as selection-aware thresholds, while row actions can now use named disable hooks. See the reference for the full schema.
4. Integrate with other workflows
- Signals or admin – Import the same async helpers (Async Manager) to queue work or enforce locks outside PowerCRUD.
- Notifications – Override
async_task_lifecyclein your manager to send emails/slack messages onfail/complete. - Audit logging – Hook into lifecycle events or override CRUD methods such as
persist_single_object(...)/persist_bulk_update(...)to push entries to your logging system.
5. Useful references
- Configuration options – complete list of view settings and defaults.
- Hooks reference – canonical contracts for the main public override points.
- Management commands – template copy, Tailwind safelist, async cleanup.
- Sample app – full working example you can mine for patterns.
Next steps
You now have the full toolkit: core CRUD, synchronous and async bulk operations, reusable async helpers, dashboards, styling, and customisation. Refer back to the guides as needed and keep the reference section handy for specific settings or commands. Happy shipping!