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 sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. 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": {} }