mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-15 21:27:23 -04:00
663ed1a077
The latest flake8 seems to look for these and they are in fact correctable with a backslash. Also need to add r to the strings to avoid W605. Change-Id: I8045309aa2ad29978ba7e99c45f75bc1457dff3d
116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
"""Illustrates use of the :meth:`.AttributeEvents.init_scalar`
|
|
event, in conjunction with Core column defaults to provide
|
|
ORM objects that automatically produce the default value
|
|
when an un-set attribute is accessed.
|
|
|
|
"""
|
|
|
|
from sqlalchemy import event
|
|
|
|
|
|
def configure_listener(mapper, class_):
|
|
"""Establish attribute setters for every default-holding column on the
|
|
given mapper."""
|
|
|
|
# iterate through ColumnProperty objects
|
|
for col_attr in mapper.column_attrs:
|
|
|
|
# look at the Column mapped by the ColumnProperty
|
|
# (we look at the first column in the less common case
|
|
# of a property mapped to multiple columns at once)
|
|
column = col_attr.columns[0]
|
|
|
|
# if the Column has a "default", set up a listener
|
|
if column.default is not None:
|
|
default_listener(col_attr, column.default)
|
|
|
|
|
|
def default_listener(col_attr, default):
|
|
"""Establish a default-setting listener.
|
|
|
|
Given a class attribute and a :class:`.DefaultGenerator` instance.
|
|
The default generator should be a :class:`.ColumnDefault` object with a
|
|
plain Python value or callable default; otherwise, the appropriate behavior
|
|
for SQL functions and defaults should be determined here by the
|
|
user integrating this feature.
|
|
|
|
"""
|
|
|
|
@event.listens_for(col_attr, "init_scalar", retval=True, propagate=True)
|
|
def init_scalar(target, value, dict_):
|
|
|
|
if default.is_callable:
|
|
# the callable of ColumnDefault always accepts a context
|
|
# argument; we can pass it as None here.
|
|
value = default.arg(None)
|
|
elif default.is_scalar:
|
|
value = default.arg
|
|
else:
|
|
# default is a Sequence, a SQL expression, server
|
|
# side default generator, or other non-Python-evaluable
|
|
# object. The feature here can't easily support this. This
|
|
# can be made to return None, rather than raising,
|
|
# or can procure a connection from an Engine
|
|
# or Session and actually run the SQL, if desired.
|
|
raise NotImplementedError(
|
|
"Can't invoke pre-default for a SQL-level column default"
|
|
)
|
|
|
|
# set the value in the given dict_; this won't emit any further
|
|
# attribute set events or create attribute "history", but the value
|
|
# will be used in the INSERT statement
|
|
dict_[col_attr.key] = value
|
|
|
|
# return the value as well
|
|
return value
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
from sqlalchemy import Column, Integer, DateTime, create_engine
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
import datetime
|
|
|
|
Base = declarative_base()
|
|
|
|
event.listen(Base, "mapper_configured", configure_listener, propagate=True)
|
|
|
|
class Widget(Base):
|
|
__tablename__ = "widget"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
|
|
radius = Column(Integer, default=30)
|
|
timestamp = Column(DateTime, default=datetime.datetime.now)
|
|
|
|
e = create_engine("sqlite://", echo=True)
|
|
Base.metadata.create_all(e)
|
|
|
|
w1 = Widget()
|
|
|
|
# not persisted at all, default values are present the moment
|
|
# we access them
|
|
assert w1.radius == 30
|
|
|
|
# this line will invoke the datetime.now() function, and establish
|
|
# its return value upon the w1 instance, such that the
|
|
# Column-level default for the "timestamp" column will no longer fire
|
|
# off.
|
|
current_time = w1.timestamp
|
|
assert current_time > datetime.datetime.now() - datetime.timedelta(
|
|
seconds=5
|
|
)
|
|
|
|
# persist
|
|
sess = Session(e)
|
|
sess.add(w1)
|
|
sess.commit()
|
|
|
|
# data is persisted. The timestamp is also the one we generated above;
|
|
# e.g. the default wasn't re-invoked later.
|
|
assert sess.query(Widget.radius, Widget.timestamp).first() == (
|
|
30,
|
|
current_time,
|
|
)
|