Option to ignore extra keywords in an sqlalchemy Mapped Class constructor?

sqlalchemy mapper
sqlalchemy eager load
sqlalchemy select columns
sqlalchemy join
sqlalchemy python
sqlalchemy orm
sqlalchemy subquery
sqlalchemy create database

Per below, I am trying initialize a sqlalchemy Mapped Class from a python dictionary that has extra keys. Is it possible to have the Mapped Class automatically ignore the extra keys instead of throwing an error? Likewise, can the Mapped Class have default values if the keys are not present?

from sqlalchemy import Column, Integer, String
class User(Base):
     __tablename__ = 'users'

     id = Column(Integer, primary_key=True)
     name = Column(String)

And here is the init part:

my_example_user = {'id'=1, 'name'='john', 'extra_key'= 1234}
User(**my_example_user)

Which throws an invalid key error

Thoughts?


In short, define constructor which does not pass arguments up to its superclass:

class User(Base):

    # ...

    def __init__(self, **entries):

        # NOTE: Do not call superclass
        #       (which is otherwise a default behaviour).
        #super(User, self).__init__(**entries)

        self.__dict__.update(entries)

I hit the same problem in transition from peewee which requires the opposite - to pass arguments to its superclass (and, therefore, constructor was already defined). So, I just tried commenting the line out and things start to work.

UPDATE

Also, make sure that entries do not contain (and, therefore, overwrite) any meta field in User class defined for SQLAlchemy defined, for example, those ORM relationships. It's kind of obvious (SQLAlchemy), but when mistake is made, it might not be easy to spot the problem.

Class Mapping API, Option to ignore extra keywords in an sqlalchemy Mapped Class constructor? a sqlalchemy Mapped Class from a python dictionary that has extra keys. Eric Ihli. Senior Software Engineer 4 Option to ignore extra keywords in an sqlalchemy Mapped Class constructor?


SQLAlchemy Mapper objects have an attrs property which is a dictionary of the names of the fields of your mapped class.

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import class_mapper
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String)

user = {
    'name': 'Eihli',
    'skill': 11
}

user_mapper = class_mapper(User)
mapped_user = User(**user)
# Boom! TypeError: 'skill' is an invalid keyword argument for User

mapped_user = User(**{
    k: v for k, v in user.items()
    if k in user_mapper.attrs.keys()
})
# Success!

No need to mess around with maintaining an exclude lists or mucking about with dict or getting in the way of super calls.

If you're trying to generate models with nested data, you'll have to do things a little different. Otherwise you'll get an "Unhashable type 'dict'" error.

Here's an example of a helper to inspect the mapper and get the keys of the relationships.

def from_json(model, data):
    mapper = class_mapper(model)
    keys = mapper.attrs.keys()
    relationships = inspect(mapper).relationships
    args = {k: v for k, v in data.items()
            if k in keys and k not in relationships}
    return model(**args)

Object Relational Tutorial, Other options are passed to mapper() using the __mapper_args__ class variable​: A mapped class or the corresponding Mapper of one indicating a “'selectin'” - specifies that when instances of this class are loaded, an additional SELECT will be object instance (must be given as a keyword arg). Constructors and Object Initialization¶. Mapping imposes no restrictions or requirements on the constructor (__init__) method for the class.You are free to require any arguments for the function that you wish, assign attributes to the instance that are unknown to the ORM, and generally do anything else you would normally do when writing a constructor for a Python class.


Also to pass extra keywords and call Base.__init__() method you can exclude extrakeys from super() and after that do what you want:

from sqlalchemy import Column, Integer, String

class User(Base):
     __tablename__ = 'users'

     id = Column(Integer, primary_key=True)
     name = Column(String)

     def __init__(self, **kwargs):
         extra_kw_list = ['key1', 'key2']
         super(User, self).__init__(**{x: y for x, y in kwargs.items()
                                       if x not in extra_kw_list})
         #do something you need here
         item1, item2 = kwargs['key1'], kwargs['key2']

Relationship Loading Techniques, The echo flag is a shortcut to setting up SQLAlchemy logging, which is The “​instrumented” mapped class will provide us with the means to refer to our table has been provided with a constructor (e.g. __init__() method) which automatically accepts keyword names that match the columns we've mapped. First, you asked "What sort of trickery is SQLAlchemy doing". There is a bit more going on behind the scenes using a concept called Metaclasses to dynamically create the Base class. But in reality all you need to know is that SQLAlchemy is defining a constructor (albeit in a roundabout way) in the Base class that dynamically sets the elements.


Are we guaranteed that the __init__ of the superclass which is in place will never have other desired effects than setting the __dict__ entries? I didn't feel quite comfortable bypassing the superclass call completely, so my attempt at solving this was as follows, passing on only the entries which correspond to column names:

class User(Base):

    # ...

    def __init__(self, **entries):
        '''Override to avoid TypeError when passed spurious column names'''
        col_names = set([col.name for col in self.__table__.columns])
        superentries = {k : entries[k] for k in col_names.intersection(entries.keys())}
        super().__init__(**superentries)

ORM Events, joined loading - available via lazy='joined' or the joinedload() option, this form of rather than as a default loading option on the mapping, in particular when As additional rows are received for a User object just loaded in a previous If the “​eager” portion of the statement is “aliased”, the alias keyword  mgcdanny. Apparently, this user prefers to keep an air of mystery about them. 7 Option to ignore extra keywords in an sqlalchemy Mapped Class constructor? Nov 18


Based on R Yakovlev's answer, you can make the list of the elements dynamic:

from sqlalchemy import Column, Integer, String

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    def __init__(self, **kwargs):
        keep_kwargs = {k: v for k, v in kwargs.items() if k in user_columns}
        super(User, self).__init__(**keep_kwargs)


user_columns = [_ for _ in User.__dict__.keys() if not _.startswith('_')]

I wanted to try find a way to embed the user_columns in the object, like with a @hybrid_property, yet not have it called every time it's used.

I expect that is possible but exceeded my time limit.

Query API, Listeners have the option to return a possibly modified version of the value, when the to be a passive observer and the return value of our function is ignored. The propagate=True flag is significant if the mapped class includes inheriting This event is used to emit additional SQL statements on the given connection as​  nochmal. Apparently, this user prefers to keep an air of mystery about them. 0 Option to ignore extra keywords in an sqlalchemy Mapped Class constructor? Jul 4


Describing Databases with MetaData, Return a Query with a specific 'autoflush' setting. The method here accepts mapped classes, aliased() constructs, and For fine grained control over specific columns to count, to skip the The 'fetch' strategy results in an additional SELECT statement Query.filter_by() - filter on keyword expressions. Mapping Class Inheritance Hierarchies¶. SQLAlchemy supports three forms of inheritance: single table inheritance, where several types of classes are represented by a single table, concrete table inheritance, where each type of class is represented by independent tables, and joined table inheritance, where the class hierarchy is broken up among dependent tables, each class represented by its


Session API, There are options to change this behavior such that ALTER TABLE is used instead. A Table can reference this by specifying the schema keyword argument: If no arguments are required for the type, the class of the type can be sent the Column within the application, including ORM attribute mapping;  SQLAlchemy object-relational configuration involves the combination of Table, mapper(), and class objects to define a mapped class. declarative allows all three to be expressed at once within the class declaration. As much as possible, regular SQLAlchemy schema and ORM constructs are used directly, so that configuration between “classical


Defining Constraints and Indexes, Any keyword arguments sent to the constructor itself will override the can be used to specify additional keyword arguments to the factory, Operations which proceed relative to a particular mapped class will Repeated calls to add() will be ignored. option to sessionmaker or the Session constructor. A mapped class or the corresponding Mapper of one indicating a superclass to which this Mapper should inherit from. The mapped class here must be a subclass of the other mapper’s class. When using Declarative, this argument is passed automatically as a result of the natural class hierarchy of the declared classes.