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": {}
}

Reply via email to