I'm trying to get Marshmallow-SQLAlchemy to deserialize an object with a nested object without specifying the foreign key for the nested object (which should be the primary key of the parent object). Here's a standalone example:
# Python version == 3.8.2
from datetime import datetime
import re
# SQLAlchemy == 1.3.23
from sqlalchemy import func, create_engine, Column, ForeignKey, Text, DateTime
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy.orm import relationship, sessionmaker
# marshmallow==3.10.0
# marshmallow-sqlalchemy==0.24.2
from marshmallow import fields
from marshmallow.fields import Nested
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
################################################################################
# Set up
################################################################################
engine = create_engine("sqlite:///test.db")
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
################################################################################
# Models
################################################################################
@as_declarative()
class Base(object):
@declared_attr
def __tablename__(cls):
# From https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case
name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', cls.__name__)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
@declared_attr
def updated(cls):
return Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False)
class Account(Base):
id = Column(Text, primary_key=True)
name = Column(Text, nullable=False)
tags = relationship("AccountTag", backref="account")
class AccountTag(Base):
account_id = Column(Text, ForeignKey('account.id'), primary_key=True)
Key = Column(Text, primary_key=True)
Value = Column(Text, nullable=False)
################################################################################
# Schemas
################################################################################
class AutoSchemaWithUpdate(SQLAlchemyAutoSchema):
class Meta:
load_instance = True
sqla_session = session
updated = fields.DateTime(default=lambda: datetime.now())
class AccountSchema(AutoSchemaWithUpdate):
class Meta:
model = Account
include_relationships = True
tags = Nested("AccountTagSchema", many=True)
class AccountTagSchema(AutoSchemaWithUpdate):
class Meta:
model = AccountTag
include_fk = True
################################################################################
# Test
################################################################################
Base.metadata.create_all(engine)
account_object = AccountSchema().load({
"id": "ABC1234567",
"name": "Account Name",
"tags": [
{
"Value": "Color",
"Key": "Blue"
}
]
})
session.merge(account_object)
session.commit()
And here's the error I'm getting:
Traceback (most recent call last):
File "example.py", line 88, in <module>
account_object = AccountSchema().load({
File "C:\python\site-packages\marshmallow_sqlalchemy\schema\load_instance_mixin.py", line 92, in load
return super().load(data, **kwargs)
File "C:\python\site-packages\marshmallow\schema.py", line 727, in load
return self._do_load(
File "C:\python\site-packages\marshmallow\schema.py", line 909, in _do_load
raise exc
marshmallow.exceptions.ValidationError: {'tags': {0: {'account_id': ['Missing data for required field.']}}}
I feel like I'm trying to do something intuitive but I'm not sure anymore. I'm sure that I'm close here but am having no luck getting this to work. Help is much appreciated.