-
Notifications
You must be signed in to change notification settings - Fork 434
Description
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:
-
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)
-
Define the mixin without metaclass
from pynamodb.attributes import AttributeContainerMeta class AuditMixin(metaclass=AttributeContainerMeta): created_by = CreatedByAttribute(null=True)
-
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)
-
Import the model (e.g. in your Chalice
app.py)from chalicelib.models.company.company import Company
-
Observe the error on import
AttributeError: 'CreatedByAttribute' has no attribute 'attr_name'. Did you mean: 'attr_type'?
Expected Behavior:
- PynamoDB should recognize
CreatedByAttributeas aMapAttribute, 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
AttributeContainerMetaat the time its attributes are defined, the nestedMapAttributelacksattr_name. When theModelmetaclass runs, it calls_initialize_attributes, encounters the missing property, and raises anAttributeError.
Root Cause:
- PynamoDB only initializes attribute metadata (including
attr_name) when a class is created with its internal metaclass (AttributeContainerMeta), which normally happens forModeland for subclasses ofMapAttributedeclared directly on a class using that metaclass. Mixins without that metaclass skip this initialization.
Workarounds / Proposed Solution:
-
Ensure mixins use the correct metaclass so that all nested attributes are initialized up-front:
class AuditMixin(metaclass=AttributeContainerMeta): created_by = CreatedByAttribute(null=True)
-
Extract mixins into a separate module that is imported before any models, to guarantee the metaclass runs before model definitions.
-
(Library change) Consider enhancing PynamoDB so that any
MapAttributeencountered 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
AttributeContaineror declared with the PynamoDB metaclass. - Directly declaring
MapAttributefields on aModelworks as expected.