marshmallow-code/marshmallow-sqlalchemy

Question: using SQL Expression Language table definitions

meshantz opened this issue · 2 comments

Is this a sane pattern to follow to make use of SQLAlchemyAutoSchema with a non-orm (SQL Expression Language) table definition?

from sqlalchemy import Table, Column, Integer, String, MetaData
from sqlalchemy.orm import mapper
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema

meta = MetaData()

example_table = Table('example', meta,
    Column('id', Integer, primary_key=True),
    Column('some_field', String),
)

def make_orm(table):
    class TableRedef:
        pass

    mapped = mapper(TableRedef, table)
    TableRedef.__mapper__ = mapped

    return TableRedef

class ExampleSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = make_orm(example_table)

If there's a better pattern, please let me know. If this is good, I can make a PR to include it in the examples documentation.

Hey, I'm not a maintainer but only came across your question today, I know it's a bit of a late response!

There isn't any prose section of the docs that addresses this but in the api reference for SQLAlchemyAutoSchema this example exists:

from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field

from mymodels import User

class UserSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = User
        # OR
        # table = User.__table__

    created_at = auto_field(dump_only=True)

Notice the commented out part table = User.__table__, which looks to me like you could do this:

class ExampleSchema(SQLAlchemyAutoSchema):
    class Meta:
        table = example_table

...I haven't tested it but if that didn't work it would be a bug IMO.

Support for generating schemas directly from tables comes from here:

def fields_for_table(
self,
table,
*,
include_fk=False,
fields=None,
exclude=None,
base_fields=None,
dict_cls=dict,
):
result = dict_cls()
base_fields = base_fields or {}
for column in table.columns:
key = self._get_field_name(column)
if self._should_exclude_field(column, fields=fields, exclude=exclude):
# Allow marshmallow to validate and exclude the field key.
result[key] = None
continue
if not include_fk and column.foreign_keys:
continue
# Overridden fields are specified relative to key generated by
# self._get_key_for_column(...), rather than keys in source model
field = base_fields.get(key) or self.column2field(column)
if field:
result[key] = field
return result

Better late than never! Thanks for the reply.

I'll have to take a look and see if that works. I'll follow up when I get the chance. If it does, it would be nice if it were more prominent in the docs.