Skip to content

angee.compose.rendering

Concrete-model source planning, validation, and rendering.

The renderer owns every fact needed to turn Runtime's discovered composition into concrete Django model source. It receives plain grouped declarations and never reaches back into Runtime or its generated-tree lifecycle.

RuntimeModelRenderPlan

python
@dataclass(frozen=True, slots=True)
class RuntimeModelRenderPlan()

Named render plan for one concrete runtime model class.

child_overrides_parent

Whether this materialized child emits its abstract source before its concrete parent.

True only for a child declaring child_overrides_parent (F-e); it flips the base tuple to [donors] → source → parent so the source's methods win the MRO natively. False keeps the parent-first status quo.

override_removed_fields

Parent-shared abstract fields the flipped child re-declares as None.

Empty unless child_overrides_parent. Child-first emission lists the source before the concrete parent, so Django copies the source's fields — including the framework fields it flattened from shared abstract ancestors (created_at/updated_at) — as local before the MTI parent link would dedup them, duplicating the parent's columns. Re-declaring each as None drops the copy so the child inherits the parent's column and the emitted schema matches the parent-first order.

after_resource_load_aliases

Composed-base aliases whose after_resource_load the concrete model aggregates.

Empty for the single-donor status quo — one implementation resolves natively through the concrete model's MRO, so the composer emits nothing new.

ModelRenderer

python
class ModelRenderer()

Plan, validate, and render concrete runtime model sources.

__init__

python
def __init__(*, sources_by_label: dict[str, tuple[type[AngeeModel], ...]],
             source_models_by_composition_label: dict[str, type[AngeeModel]],
             extensions: dict[str, tuple[type[AngeeModel],
                                         ...]], runtime_module: str) -> None

Bind the complete discovered composition needed for rendering.

validate

python
def validate() -> None

Validate render-specific child override contracts once.

render

python
def render(label: str, source_models: tuple[type[AngeeModel], ...]) -> str

Return concrete model source for one target label.

This is what makes a source addon's abstract models real. For each source model it emits a concrete class that imports the abstract source (aliased Abstract<Name>), any same-row extends extension bases, and, for runtime = True materialized children, the concrete generated parent model named by extends. It lists extension bases first, then the concrete parent when present, then the source, and pins Meta.abstract = False with app_label = label — so the generated class registers under the source addon's label when the composer imports runtime.<label>.models. Django-owned Meta facts ride along through class Meta(_SourceMeta); REBAC Meta options are re-emitted because Django discards non-standard Meta attributes. Mixins may contribute model decorators and class-body attributes through declared emission seams. Field collisions across the composed bases are rejected at construction (_check_field_collisions).

extension_bases

python
def extension_bases(
        model_class: type[AngeeModel]) -> tuple[type[models.Model], ...]

Return the abstract bases same-row extensions contribute to model_class.

child_override_removed_fields

python
def child_override_removed_fields(
        child_class: type[AngeeModel]) -> tuple[str, ...]

Return the parent-shared abstract fields a flipped child must drop (F-e).

A child that flips to child-first re-contributes the fields it flattened from shared abstract ancestors (created_at/updated_at) as local, duplicating the concrete parent's columns (RuntimeModelRenderPlan). The emitted class shadows each with None to inherit the parent's column instead. Only fields the child inherited from an abstract base and the parent also owns are dropped — the child's own new fields stay.

Shadowing is only sound when the child's copy and the parent's column are the same field: a same-name field the child deliberately redefined would vanish behind the None shadow, silently dropping the override. Each dropped field is proven identical by deconstruct() first, so a genuine divergence fails the build loudly rather than disappearing.

declared_fields

python
def declared_fields(model_class: type[models.Model]) -> tuple[str, ...]

Return fields directly declared by one abstract composition base.

Released under the AGPL-3.0 License.