Ability to ignore additional keywords in the Mapped Class sqlalchemy constructor?

Below, I am trying to initialize a sqlalchemy-based SQL class from a python dictionary with additional keys. Is it possible that the Mapped Class automatically ignores additional keys, rather than throwing an error? Similarly, can the mapped class have default values ​​if there are no keys?

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) 

What causes the wrong key error

Thoughts?

+5
source share
4 answers

In short, define a constructor that does not pass arguments 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 got into the same problem when switching from peewee , which requires the opposite - pass arguments to its superclass (and, therefore, the constructor has already been defined). So, I just tried commenting out the line and everything starts to work.

UPDATE

Also, make sure that entries do not contain (and therefore rewrite) any meta field in the User class defined for SQLAlchemy, defined for example, these ORM relationships. This is obvious (SQLAlchemy), but when an error is made, it can be difficult to detect the problem.

+1
source

In addition, to pass additional keywords and call the Base.__init__() method, you can exclude the extra keys 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'] 
+1
source

We guarantee that the __init__ superclass that is in place will never have other desired effects than setting __dict__ records? I did not feel quite comfortable completely bypassing the superclass call, so my attempt to solve this was as follows, passing only the entries corresponding to the 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) 
0
source

Based on Yakovlev’s answer, you can make a list of dynamic elements:

 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 to find a way to embed user_columns into an object, for example using @hybrid_property, but not call it every time it is used.

I expect it is possible, but exceeded my deadline.

0
source

Source: https://habr.com/ru/post/1236265/


All Articles