Inline & Specialized Views
Developer guide for the Enezaframework. Part of the architecture series.
Table of Contents
- Overview
- Inline Views
- One-to-Many Inline Views (List + Detail)
BaseInlineOneToOneDetailView- Specialized Views
- Journals Views
- Setup Views
- Document Views
- Inline Template Context
- Critical Rules
- Related Guides
Overview
Beyond the core CRUD views, Enezaframework provides two families of views:
- Inline views manage records that belong to a parent record (one-to-many or one-to-one) and are rendered inside the parent's detail page.
- Specialized views behave like the core views but are tailored to a class of models (journals, setups, documents).
classDiagram
class BaseDetailView
class BaseListView
class BaseCreateView
class BaseDeleteView
class BaseInlineListView
class BaseInlineDetailView
class BaseInlineCreateView
class BaseInlineDeleteView
class BaseInlineOneToOneDetailView
class BaseJournalsListView
class BaseSetupsListView
class BaseSourceDocumentListView
BaseInlineListView --|> BaseListView
BaseInlineDetailView --|> BaseDetailView
BaseInlineCreateView --|> BaseCreateView
BaseInlineDeleteView --|> BaseDeleteView
BaseInlineOneToOneDetailView --|> BaseInlineDetailView
BaseJournalsListView --|> BaseListView
BaseSetupsListView --|> BaseListView
BaseSourceDocumentListView --|> BaseListViewInline Views
| View | Purpose |
|---|---|
BaseInlineListView |
Lists child records for a parent. |
BaseInlineDetailView |
Shows/edits one child record. |
BaseInlineCreateView |
Creates a child record under a parent. |
BaseInlineDeleteView |
Deletes a child record. |
BaseInlineOneToOneDetailView |
Shows/edits a strictly one-to-one related object. |
Inline views are usually invoked by the parent detail view using HTMX, which swaps their content into an inline tab. The parent declares them in get_inline_related_objects; see Field & Inline UI Configuration.
One-to-Many Inline Views (List + Detail)
A child record that belongs to a parent (one-to-many) is edited inside the parent's detail page with two
views: a BaseInlineListView that lists the children, and a BaseInlineDetailView that shows/edits one
child. The parent declares the pair in get_inline_related_objects (see
Field & Inline UI Configuration); the framework loads each view over
HTMX into an inline tab.
from enezaframework.views import BaseInlineListView, BaseInlineDetailView
from enezaframework.utils import auto_url_register
from appname.models import ChildModel, ParentModel
@auto_url_register
class ChildModelInlineListView(BaseInlineListView):
model = ChildModel
parent_model = ParentModel
related_name = "children"
partial_template_name = "appname/partials/child_inline_list.html"
url_name = "childmodel_inline"
def set_list_fields(self):
return ["field1", "field2"]
def set_search_fields(self):
return ["field1", "parent__name"]
@auto_url_register
class ChildModelInlineDetailView(BaseInlineDetailView):
model = ChildModel
parent_model = ParentModel
related_name = "children"
template_name = "appname/partials/child_inline_detail.html"
url_name = "childmodel_inline"
def detail_ui_view(self):
return [
{
"name": "Report",
"icon": "Home",
"Fieldcolumns": [{"fields": ["parent"]}],
},
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# super() already sets main_object_pk and the related_object_* URL names.
# Do not override main_object_pk here.
return context
Required attributes:
| Attribute | View | Description |
|---|---|---|
model |
list, detail | The inline model class. |
parent_model |
list, detail | The parent model class (not a string). |
related_name |
list, detail | The reverse FK/O2O relation name from the child back to the parent. |
set_list_fields() |
list | Return the columns to show. Raises NotImplementedError if missing. |
set_search_fields() |
list | Return the searchable fields. Raises NotImplementedError if missing. |
detail_ui_view() |
detail | Form layout (same schema as core detail views). |
Optional attributes:
| Attribute | View | Default | Description |
|---|---|---|---|
partial_template_name |
list | core/partials/inline_list_partial.html |
Custom list template. |
template_name |
detail | core/partials/inline_update_partial.html |
Custom detail template. |
url_name |
list, detail | (model name) | URL-prefix override. Must match the parent's inline url_name. |
set_search_fields()must be implemented, but the base list view does not currently apply it inget_queryset()— no search filtering happens out of the box.
The list view's get_queryset() filters children by the parent (main_object_pk) through
find_field_by_related_name() (handles ForeignKey, OneToOne, ManyToMany, and GenericForeignKey) and by
the current entity when the model has an entity field; results are ordered by -created_at.
BaseInlineOneToOneDetailView
Specialized for a related object in a strict one-to-one relationship with the parent.
Required attributes:
| Attribute | Description |
|---|---|
model |
The inline model class. |
parent_model |
The parent model class. |
related_name |
The related_name on the FK/O2O from the inline model back to the parent. |
detail_ui_view |
Form layout (same schema as core detail views). |
from enezaframework.views import BaseInlineOneToOneDetailView
from enezaframework.utils import auto_url_register
from accountpayable.models import VendorTable, VendorTableDetails
@auto_url_register
class VendorTableDetailsInlineView(BaseInlineOneToOneDetailView):
model = VendorTableDetails
parent_model = VendorTable
related_name = "details"
def detail_ui_view(self):
Fieldtabs = [
{
"name": "Purchase demographics",
"icon": "Home",
"Fieldcolumns": [
{"fields": ["segment"]},
],
},
]
return Fieldtabs
For conditional or generic one-to-one inlines, the parent view supplies get_related_object_id to resolve the object being edited. Match the related_name used here with the relation_name declared in the parent's get_inline_related_objects.
Specialized Views
General pattern
Specialized views subclass the core views and add behavior (default filters, read-only enforcement, document layout). The child view still sets model and detail_ui_view, and is still decorated with @auto_url_register.
Journals Views
For models that inherit from a journal header/line base (for example JournalBatch and its lines).
| View | Purpose |
|---|---|
BaseJournalsListView |
Lists journal headers. |
BaseJournalsDetailView |
Shows a journal header with lines. |
BaseJournalsCreateView |
Creates a journal header. |
BaseJournalsDeleteView |
Deletes a journal header. |
BaseJournalsLinesListView |
Lists journal lines. |
BaseJournalsLinesDetailView |
Shows/edits a journal line. |
BaseJournalsLinesCreateView |
Creates a journal line. |
BaseJournalsLinesDeleteView |
Deletes a journal line. |
Line views are typically used as inlines under BaseJournalsDetailView. Note that BaseJournalsDetailView and BaseJournalsLinesDetailView define action_list = [] and load inlines through get_inline_related_objects, so override those the same way you would on a core detail view.
Setup Views
For models that inherit from EnezaSetup (see Base Models & Model Types).
| View | Purpose |
|---|---|
BaseSetupsListview |
Lists setup records. |
BaseSetupsDetailView |
Shows/edits a setup record. |
BaseSetupsCreateView |
Creates a setup record. |
BaseSetupsDeleteView |
Deletes a setup record. |
Read-only variants
| View | Purpose |
|---|---|
BaseSetupsReadOnlyListView |
Lists setup records without edit affordances. |
BaseSetupsReadOnlyDetailView |
Shows setup records without allowing modification. |
Use the read-only variants when setup data must be visible but not editable in a given context or for a given role.
from enezaframework.views import BaseSetupsListview
from enezaframework.utils import auto_url_register
from accountpayable.models import VendorPostingGroup
@auto_url_register
class VendorPostingGroupListView(BaseSetupsListview):
model = VendorPostingGroup
def set_list_fields(self):
list_fields = ["code", "description"]
return list_fields
def set_search_fields(self):
search_fields = ["code", "description"]
return search_fields
The class name is
BaseSetupsListview(lowercasev). Import it fromenezaframework.views.
Document Views
For "source document" models (sales orders, purchase invoices, and similar) that need a header/lines layout.
| View | Purpose |
|---|---|
BaseSourceDocumentListView |
Lists source documents. |
BaseSourceDocumentDetailView |
Shows a document with header and lines. |
BaseSourceDocumentCreateView |
Creates a source document. |
BaseSourceDocumentDeleteView |
Deletes a source document. |
These views support the headerpostion tab key ("header" or "lines") so a tab's content renders in the document header region or alongside the lines.
def detail_ui_view(self):
Fieldtabs = [
{
"name": "General",
"icon": "Home",
"headerpostion": "header",
"Fieldcolumns": [
{"fields": ["document_no", "customer"]},
],
},
{
"name": "Lines",
"icon": "Home",
"headerpostion": "lines",
"Fieldcolumns": [
{"fields": ["total_amount", "currency"]},
],
},
]
return Fieldtabs
Inline Template Context
The base inline views populate the template context with routing and identity variables. Use these in custom inline templates instead of hard-coding URL names.
| Variable | List | Detail | Description |
|---|---|---|---|
main_object_pk |
✓ | ✓ | The parent's primary key (from the main_object_pk GET parameter). |
related_object_list_url_name |
✓ | ✓ | List URL name (from UrlMixin.get_list_url_name()). |
related_object_detail_url_name |
✓ | ✓ | Detail URL name. |
related_object_create_url_name |
✓ | ✓ | Create URL name. |
related_object_delete_url_name |
✓ | ✓ | Delete URL name. |
related_object_container_name |
✓ | ✓ | Container id used in hx-target (see note below). |
related_object_verbose_name |
✓ | ✓ | Model verbose name. |
related_object_verbose_name_plural |
✓ | ✓ | Model verbose name plural. |
related_object_list |
✓ | — | The child list (object_list). |
related_object_current_object |
— | ✓ | The child object being viewed/edited. |
current_app |
✓ | ✓ | The current app namespace. |
party_object |
✓ | ✓ | The Party instance for party inlines (else None). |
related_object_container_namediffers between the two views: the list view sets it tomodel._meta.model_name, while the detail view sets it tomodel._meta.verbose_name.lower().replace(" ", ""). Keep this in mind if your list and detail templates share anhx-targetid.
The inline detail template's back button uses these variables:
<button type="button"
hx-get="{% url current_app|add:':'|add:related_object_list_url_name %}?main_object_pk={{ main_object_pk }}"
hx-target="#{{ related_object_container_name }}-content">
Back to List
</button>
Critical Rules
- Decorate both inline views with
@auto_url_register; do not add manual URL patterns for them inurls.py— the registry generates the routes (see URL Auto-Registration). parent_modelmust be the model class (for exampleRisk), not a string like"riskmanagement.Risk".- Do not override
get_list_url_name(),get_detail_url_name(),get_create_url_name(), orget_delete_url_name()—UrlMixinderives them from the model name /url_name. - Do not override
main_object_pkinget_context_data()— the base view sets it from the request GET parameter. url_namemust match between the view class attribute and theurl_namekey in the parent'sget_inline_related_objects()entry.related_nameon the view must matchrelation_namein the parent's inline config.- Import from
enezaframework.views, notenezaframework.views.baseinline.