Hi Michael et al,
tl;dr: I would like ClassManager.new_instance() to be made official as
discussed in the old discussion and suggest it should initialize the
polymorphic identity.
Context
=======
I am not sure if anybody remembers the discussion under
https://groups.google.com/d/msg/sqlalchemy/8kbu8jL0QII/1dWTRp_DVYYJ
where I tried to recreate objects not by unpickling but from my own
serialization code.
With SQLAlchemy I could use MyClass.__new__ to create new instances
which I would then populate. That failed with SQLAlchemy 0.7 and I
thought about using ClassManager.new_instance()
Turns out that I never did. Instead my code currently needlessly
initializes each instance using the __init__ method only to drop that
information in favor of the deserialized data.
What's worse: __init__ now doesn't initialize new instances, instead the
code lazily initializes fields before writing to the database or on
first access via a property - sic!
Needless to say I would like to get rid of it. Trying to do so already
uncovered a bug in the software, where creation times are lost during
deserialization ending up as the unix epoch.
Example code
============
Basically I would like to initialize instances like this:
---------
mapper = class_mapper(Circle)
circle = mapper.class_manager.new_instance()
circle.radius = 42
session.add(circle)
session.commit()
---------
This fails to initialize the target_type field of Shape (which Circle
inherits from). Therefore, new_instance works fine for concrete mapped
classes, unless polymorphism is used.
Running the attached example gives this output:
> $ pipenv install
> [...]
> $ pipenv run python example.py
>
> This works (as expected)
>
> This fails (unexpectedly)
> -> Error is IntegrityError('(sqlite3.IntegrityError) NOT NULL constraint
> failed: shape.target_type',)
>
> This works (but is a bit ugly)
Would you please consider documenting this as a supported use case and
potentially extend new_instance to set the polymorphic_identity?
Thanks a bunch, especially for your hard work in creating SQLAlchemy!
Greetings, Torsten
--
$---+----1----+----2----+----3----+----4----+----5----+----6----+
SCALE GmbH
Niederlassung Dresden
Torsten Landschoff
Pohlandstraße 19
01309 Dresden
Tel: +49-351-312002-10
Fax: +49-351-312002-29
SCALE GmbH
Registergericht und Sitz: Ingolstadt, HRB 6384
Geschäftsführer: Dr.-Ing. Heiner Müllerschön, Dipl.-Math. Ulrich Franz
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
from sqlalchemy import Column, ForeignKey, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import class_mapper, sessionmaker
# Interesting stuff starts at "Evaluate ClassManager.create_instance"
Base = declarative_base()
class Company(Base):
__tablename__ = "company"
id = Column(Integer, primary_key=True)
ticker = Column(String)
name = Column(String)
class Shape(Base):
__tablename__ = "shape"
id = Column(Integer, primary_key=True)
target_type = Column(String(50), nullable=False)
__mapper_args__ = {"polymorphic_on": target_type}
class Circle(Shape):
__tablename__ = "circle"
id = Column(Integer, ForeignKey(Shape.id), primary_key=True)
radius = Column(Integer)
__mapper_args__ = {"polymorphic_identity": "circle"}
engine = create_engine("sqlite:///", echo=None)
Base.metadata.create_all(engine)
session = sessionmaker(engine)()
# Evaluate ClassManager.create_instance
#---------------------------------------
print("\nThis works (as expected)")
mapper = class_mapper(Company)
company = mapper.class_manager.new_instance()
company.name = "Red Hat, Inc."
company.ticker = "RHAT"
session.add(company)
session.commit()
# SQL: INSERT INTO company (ticker, name) VALUES (?, ?)
# VARS: ('RHAT', 'Red Hat, Inc.')
print("\nThis fails (unexpectedly)")
try:
mapper = class_mapper(Circle)
circle = mapper.class_manager.new_instance()
circle.radius = 42
session.add(circle)
session.commit()
# SQL: INSERT INTO shape (target_type) VALUES (?)
# VARS: (None,)
except Exception as e:
print("-> Error is {0!r}".format(e))
session.rollback()
print("\nThis works (but is a bit ugly)")
mapper = class_mapper(Circle)
circle = mapper.class_manager.new_instance()
setattr(circle, mapper.polymorphic_on.name, mapper.polymorphic_identity)
circle.radius = 42
session.add(circle)
session.commit()
# SQL: INSERT INTO shape (target_type) VALUES (?)
# VARS: ('circle',)
# SQL: INSERT INTO circle (id, radius) VALUES (?, ?)
# VARS: (1, 42)
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[dev-packages]
[packages]
sqlalchemy = "*"
[requires]
python_version = "2.7"
{
"_meta": {
"hash": {
"sha256":
"4d66bfd00eb47ff008212eeb658412d27890441c52b45a81e41e98d9b3f10057"
},
"host-environment-markers": {
"implementation_name": "cpython",
"implementation_version": "0",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "4.11.0-14-generic",
"platform_system": "Linux",
"platform_version": "#20~16.04.1-Ubuntu SMP Wed Aug 9 09:06:22 UTC
2017",
"python_full_version": "2.7.12",
"python_version": "2.7",
"sys_platform": "linux2"
},
"pipfile-spec": 5,
"requires": {
"python_version": "2.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"sqlalchemy": {
"hashes": [
"sha256:8b79a5ed91cdcb5abe97b0045664c55c140aec09e5dd5c01303e23de5fe7a95a"
],
"version": "==1.1.15"
}
},
"develop": {}
}