Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply Column defaults before a commit in sqlalchemy

Tags:

I have a declarative-base model:

class User(Base):     id = Column(Integer, primary_key=True)     money = Column(Integer, default=100) 

and then I run

>>> u = User() >>> u.money None 

How can I populate the defaults using sqlalchemy without writing anything to the database?

like image 608
Elrond Avatar asked Dec 09 '12 20:12

Elrond


People also ask

Are SQLAlchemy columns nullable by default?

Columns are nullable by default The default value of SQLAlchemy nullable is False unless it's a primary key. A foreign key is also nullable by default.

What does First () do in SQLAlchemy?

first() applies a limit of one within the generated SQL, so that only one primary entity row is generated on the server side (note this may consist of multiple result rows if join-loaded collections are present). Calling Query. first() results in an execution of the underlying query.

What is SQLAlchemy Autocommit?

execute(text("INSERT INTO users VALUES (1, 'john')")) # autocommits. The “autocommit” feature is only in effect when no Transaction has otherwise been declared. This means the feature is not generally used with the ORM, as the Session object by default always maintains an ongoing Transaction .

What is server_default in SQLAlchemy?

server_default is specifically the SQL text that one would specify in the "DEFAULT" section of a "CREATE TABLE" statement, and SQLAlchemy has no special expectations in this area, it accepts SQL text like any other non-expression portion where a string is sent.


2 Answers

The column default only applies to INSERT and UPDATE statements, and thus isn't being applied until you .flush() the session.

To see the same value on new instances before you flush, you need to apply the default when a new instance is being created; in the __init__ method of the User object:

class User(Base):     __tablename__ = 'users'      def __init__(self, **kwargs):         if 'money' not in kwargs:              kwargs['money'] = self.__table__.c.money.default.arg         super(User, self).__init__(**kwargs)      id = Column(Integer, primary_key=True)     money = Column(Integer, default=100) 

If no money attribute is being set, we add one directly based on the default configured for the column.

Note that the defaults are SQL expressions, not Python values, so you may have to map those to Python objects first. For example, a boolean field will have a default 'false' or 'true' string value, not a False or True Python boolean object.

like image 129
Martijn Pieters Avatar answered Sep 27 '22 19:09

Martijn Pieters


I have discovered a mechanism for populating some defaults automatically when an instance is created. I'm migrating from an in-house ORM that set defaults at instance creation, so I needed to preserve this behaviour and I didn't want to touch every model definition.

BaseModel = declarative_base()  class Base(BaseModel):     __abstract__ = True      def __init__(self, **kwargs):         for attr in self.__mapper__.column_attrs:             if attr.key in kwargs:                 continue              # TODO: Support more than one value in columns?             assert len(attr.columns) == 1             col = attr.columns[0]              if col.default and not callable(col.default.arg):                 kwargs[attr.key] = col.default.arg          super(Base, self).__init__(**kwargs) 

The major limitation is that only non-callable defaults are supported. Callable defaults and those that use a database query require an execution context that isn't available at instance creation time. Also, I have not discovered a case where len(ColumnProperty.columns) != 1 but that isn't supported either.

like image 27
James Emerton Avatar answered Sep 27 '22 18:09

James Emerton