Skip to content

MapAttribute in mixin fails to initialize attr_name when used in PynamoDB models #1273

@AleMazzari

Description

@AleMazzari

Description:
When defining a mixin that provides a nested attribute via MapAttribute, PynamoDB does not properly initialize the attribute metadata (attr_name, attr_path) unless the mixin itself is processed by PynamoDB’s metaclass. As a result, importing a model that inherits both Model and the mixin raises an AttributeError about missing attr_name.


Steps to Reproduce:

  1. Define your nested attribute

    from pynamodb.attributes import MapAttribute, UnicodeAttribute
    
    class CreatedByAttribute(MapAttribute):
        id          = UnicodeAttribute(null=False)
        given_name  = UnicodeAttribute(null=False)
        family_name = UnicodeAttribute(null=False)
        email       = UnicodeAttribute(null=False)
  2. Define the mixin without metaclass

    from pynamodb.attributes import AttributeContainerMeta
    
    class AuditMixin(metaclass=AttributeContainerMeta):
        created_by = CreatedByAttribute(null=True)
  3. Use the mixin in your model

    from pynamodb.models import Model
    from chalicelib.models.mixins import AuditMixin
    
    class Company(Model, AuditMixin):
        class Meta:
            table_name = "company"
        pk = UnicodeAttribute(hash_key=True)
        sk = UnicodeAttribute(range_key=True)
  4. Import the model (e.g. in your Chalice app.py)

    from chalicelib.models.company.company import Company
  5. Observe the error on import

    AttributeError: 'CreatedByAttribute' has no attribute 'attr_name'. Did you mean: 'attr_type'?
    

Expected Behavior:

  • PynamoDB should recognize CreatedByAttribute as a MapAttribute, initialize its metadata (attr_name, attr_path) when the mixin is defined, and allow models to inherit that nested attribute without error.

Actual Behavior:

  • Because the mixin is not processed by PynamoDB’s AttributeContainerMeta at the time its attributes are defined, the nested MapAttribute lacks attr_name. When the Model metaclass runs, it calls _initialize_attributes, encounters the missing property, and raises an AttributeError.

Root Cause:

  • PynamoDB only initializes attribute metadata (including attr_name) when a class is created with its internal metaclass (AttributeContainerMeta), which normally happens for Model and for subclasses of MapAttribute declared directly on a class using that metaclass. Mixins without that metaclass skip this initialization.

Workarounds / Proposed Solution:

  1. Ensure mixins use the correct metaclass so that all nested attributes are initialized up-front:

    class AuditMixin(metaclass=AttributeContainerMeta):
        created_by = CreatedByAttribute(null=True)
  2. Extract mixins into a separate module that is imported before any models, to guarantee the metaclass runs before model definitions.

  3. (Library change) Consider enhancing PynamoDB so that any MapAttribute encountered during model construction—regardless of whether it came via mixin—triggers attribute initialization automatically.


Environment:

  • PynamoDB version: e.g. 2023.2.0
  • Python version: e.g. 3.12
  • Operating System: macOS (Apple Silicon / Intel)

Additional Notes:

  • This only affects attributes provided via mixins (or intermediate base classes) that are not themselves subclasses of PynamoDB’s AttributeContainer or declared with the PynamoDB metaclass.
  • Directly declaring MapAttribute fields on a Model works as expected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions