PartyModelMixin API Reference

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

Table of Contents

  1. Overview
  2. API Table
  3. party Field
  4. party_category
  5. party_related_name
  6. get_party_defaults()
  7. _resolve_party_related_name()
  8. MRO Ordering
  9. Signal Chain
  10. Source Map
  11. Related Guides

Overview

Condensed reference for PartyModelMixin (enezaframework/models/ezaparty.py:5) and the signals that act on it.


API Table

Attribute / Method Type Description
party ForeignKey Auto-provided FK to Party (null=True, blank=True, on_delete=PROTECT).
party_category property Must override. Returns 'PRS' or 'ORG' (PartyCategory). Determines Person vs Business. Base value is None.
party_related_name class attr Optional override. The FK reverse name. Base value is None; default resolves to '%(class)s_party'.
get_party_defaults() method May override. Returns the dict for Party.objects.create(). Calls super() to inherit entity and party_type.
_resolve_party_related_name() classmethod Returns the resolved related name. Internal.

party Field

python
party = models.ForeignKey(
    'enezacommon.Party',
    null=True,
    blank=True,
    on_delete=PROTECT,
    related_name='%(class)s_party',
    verbose_name="Party",
)
  • Nullable and blank, because it is filled by the signal after the first insert.
  • on_delete=PROTECT: a Party cannot be deleted while a business record still references it.
  • Hidden from forms by UiMixin.get_form_class() (enezaframework/views/mixins.py:353-361).

party_category

python
@property
def party_category(self):
    return self.customer_type

Must return PartyCategory.PERSON ('PRS') or PartyCategory.ORGANIZATION ('ORG'). If it returns None, the signal logs a warning and skips party creation. See Party Types & Profiles.


python
party_related_name = 'billing_customers'   # optional

When unset, _resolve_party_related_name() returns '%(class)s_party', giving <modelname>_party (for example contact_party). Override only when you need a custom reverse accessor.


get_party_defaults()

Base implementation (ezaparty.py:21-30):

python
def get_party_defaults(self):
    defaults = {
        'party_type': self.party_category,
        'display_name': str(self),
        'is_active': True,
    }
    entity = getattr(self, 'entity', None)
    if entity:
        defaults['entity'] = entity
    return defaults

Override pattern:

python
def get_party_defaults(self):
    defaults = super().get_party_defaults()
    defaults['display_name'] = self.searchname or self.vendorno or 'Vendor'
    return defaults

Always call super() so party_type and entity are preserved.


python
@classmethod
def _resolve_party_related_name(cls):
    return cls.party_related_name or '%(class)s_party'

Internal helper used by the framework. Do not call it from application code.


MRO Ordering

python
class YourModel(PartyModelMixin, EnezaMaster):   # PartyModelMixin FIRST

Place PartyModelMixin before EnezaMaster so the abstract party field is contributed correctly through Django's field resolution.


Signal Chain

flowchart TD
    SAVE["YourModel.save() (created=True)"] --> FW["auto_create_party_for_party_model<br/>enezaframework/signals.py:9"]
    FW --> GUARD{"PartyModelMixin?<br/>party is None?<br/>party_category set?"}
    GUARD -->|Fail| SKIP[Skip]
    GUARD -->|Pass| PCREATE["Party.objects.create(**get_party_defaults())"]
    PCREATE --> LINK["your_model.party = party<br/>save(update_fields=['party'])"]
    LINK --> CSIG["create_org_or_person_profile<br/>enezacommon/signals.py:43"]
    CSIG --> BRANCH{"party_type"}
    BRANCH -->|PRS| PERSON["Person.objects.get_or_create(party=party)"]
    BRANCH -->|ORG| BUSINESS["Business.objects.get_or_create(party=party)"]

Source Map

Concern Location
Mixin definition enezaframework/models/ezaparty.py
Party auto-creation enezaframework/signals.py:9
Profile auto-creation enezacommon/signals.py:43
Party context and inline injection enezaframework/views/base.py:183, 200-294
Form party exclusion, inline config enezaframework/views/mixins.py:353-361, 894-927
Party bridge enezaframework/views/baseinline.py:648-693
Shared party models enezacommon/models/Party.py
Party inline views enezacommon/views/party_views.py
Party category choices enezacommon/models/choices.py:49