Party-Bridge Pattern

Developer guide for the Enezaframework party system. Part of the Party System series.

Table of Contents

  1. Overview
  2. When You Need a Bridge
  3. Auto-Detection
  4. What the Bridge Does Automatically
  5. Minimal Inline View
  6. Request Flow
  7. Related Guides

Overview

The party inline system uses shared views whose parent_model is Party. For backward compatibility or custom one-to-one inline views, you may instead declare an inline view whose parent_model is your party-based model (for example Customer). BaseInlineOneToOneDetailView bridges that gap: it walks parent → parent.party → profile and resolves the object correctly without any override.


When You Need a Bridge

Use BaseInlineOneToOneDetailView with a bridge when:

  • You are adding a custom one-to-one inline (a profile extension, a preferences row) keyed off the party.
  • You have an existing inline view whose parent_model is the business model and you do not want to repoint it at Party.
  • You need the inline's related_name to be the FK/O2O from the inline model to the party (for example person_profile).

If you are happy with the framework's shared party inlines, you do not need any of this — see Party Inline System.


Auto-Detection

BaseInlineOneToOneDetailView._is_party_bridge() (enezaframework/views/baseinline.py:652-666) returns True only when both conditions hold:

  1. The inline model (e.g., Person) has a party field (FK or OneToOne) pointing at Party.
  2. The parent model (e.g., Customer) has a party FK pointing at Party.
flowchart TD
    START[Inline view resolves object] --> M{"model has<br/>party -> Party?"}
    M -->|No| NORMAL[Use standard related_name resolution]
    M -->|Yes| P{"parent_model has<br/>party -> Party?"}
    P -->|No| NORMAL
    P -->|Yes| BRIDGE["Party bridge active<br/>field = 'party'"]

What the Bridge Does Automatically

When a bridge is detected, the base class:

  • find_field_by_related_name() → returns 'party' (baseinline.py:668-671).
  • get_object() → resolves the instance via parent_instance.party → model.objects.get(party=...), or by the URL pk if present (baseinline.py:673-684).
  • form_valid() → sets form.instance.party = parent_instance.party before saving (baseinline.py:686-693).
python
def get_object(self, queryset=None):
    if self._is_party_bridge():
        pk = self.kwargs.get('pk')
        if pk:
            return get_object_or_404(self.model, pk=pk)
        main_object_pk = self.request.GET.get('main_object_pk')
        if main_object_pk:
            parent_instance = get_object_or_404(self.parent_model, pk=main_object_pk)
            if parent_instance.party:
                return get_object_or_404(self.model, party=parent_instance.party)
        raise Http404
    return super().get_object(queryset)

Minimal Inline View

No find_field_by_related_name(), get_object(), or form_valid() overrides are needed.

python
from enezaframework.views import BaseInlineOneToOneDetailView
from enezaframework.utils import auto_url_register
from enezacommon.models import Person
from myapp.models import Customer


@auto_url_register
class CustomerPersonInlineDetailView(BaseInlineOneToOneDetailView):
    model = Person
    parent_model = Customer
    related_name = 'person_profile'

    def detail_ui_view(self):
        return [
            {
                'name': 'Personal Information',
                'icon': 'Home',
                'Fieldcolumns': [
                    {'fields': ['first_name', 'middle_name', 'last_name']},
                    {'fields': ['birthday', 'email']},
                ],
            },
        ]

related_name must match the reverse accessor from the inline model to the party — for Person it is person_profile; for Business it is business_profile.


Request Flow

sequenceDiagram
    participant Parent as Customer detail page
    participant Inline as CustomerPersonInlineDetailView
    participant Bridge as _is_party_bridge
    participant DB as Database

    Parent->>Inline: GET with main_object_pk=customer_pk
    Inline->>Bridge: check model.party and parent.party
    Bridge-->>Inline: True
    Inline->>DB: Customer.objects.get(pk=main_object_pk)
    DB-->>Inline: customer
    Inline->>DB: Person.objects.get(party=customer.party)
    DB-->>Inline: person
    Inline-->>Parent: inline form partial

    Parent->>Inline: POST with main_object_pk=customer_pk
    Inline->>Bridge: check bridge
    Inline->>DB: person = form.save(commit=False)
    Inline->>DB: form.instance.party = customer.party
    Inline->>DB: save
    Inline-->>Parent: updated inline partial