Domanda

We have a few tables which have a declarative inheritance between them and we've been trying to make the inheritance "lazy", meaning that we do not want to eagerly JOIN on the parent table.

For example:

class Entity(Base):
    __tablename__ = 'entities'
    id = Column(Integer, primary_key=True)
    entity_type = Column(String, nullable=False)
    __mapper_args__ = {'polymorphic_on': entity_type, 'polymorphic_identity': 'entities'}

class Person(Entity):
    __tablename__ = 'persons'
    person_id = Column(None, ForeignKey('entities.id'), primary_key=True)
    name = Column(String, nullable=False)
    __mapper_args__ = {'polymorphic_identity': 'persons'}

When querying Person, sqlalchemy always JOINs on Entity. I'd like that to be somehow lazy and I could not find any way to do it.

However, when querying Entity I'd still like to be able to receive Person objects if that Entity is a Person. That's why we can't simply make Person have relationships to Entity or use a Mixin which has one.

È stato utile?

Soluzione

the Person entity here is a join of Entity->Person. If you want to just query the "entities" table to start with, then query for Entity objects. If you want Entity objects that are only of type "Person", then filter on the discriminator:

persons = query(Entity).filter_by(entity_type='persons').all()

the above will return Person objects without using the join, and as you access Person-specific attributes the "persons" table will be SELECTed for each row individually.

Edit: OK, you want it the other way around. First here's how that might be possible, second is my recommendation to change your mapping.

  1. You can always get back rows from "person" by querying against the table:

    ptable = Person.__table__
    rows = query(ptable).filter(ptable.c.foo == 'bar').all()
    

    that gives you back tuples, not Person objects.

  2. Another way that might work, though I haven't tried it and it would be fairly unusual, would be to use a non-primary mapper against Person:

    from sqlalchemy.orm import mapper
    nperson = mapper(Person, Person.__table__, non_primary=True)
    people = query(nperson).filter(nperson.c.foo == 'bar').all()
    
  3. The reason inheritance favors the Entity table is because in class inheritance, Person is said to be a specialization of Entity - every Person you get is really just an Entity, with some extra qualities that make it a Person. The ORM considers the primary key of Person to be the "entities.id" column. This is because the space of all Entity subclasses spans many tables, but the full set of Entity primary keys remains exactly those values in "entities.id". The ORM also needs to see the "entities.entity_type" column here in order to see what the type of object is. If "persons" joined to a table "employees" and you had class Employee(Person), querying only the "persons" table would not only not give us the primary key of record (putting aside the FK column in persons) but also wouldn't distinguish between Person and Employee.

    Semantically, the usage request here might suggest that inheritance is not appropriate. Assuming Entity has many subclasses - Person, Animal, Vehicle, and for each it is the subclass table that is really the "data of record", whereas the columns specific to Entity are really just associated values, that suggests instead modeling the relationship as a one to one between Person and Entity. This mapping would get you exactly the loading behavior you want; a query against Person would be only against "persons", and accessing Person.entity would emit a single SQL statement to get at the corresponding Entity.

  4. Another thought is, while I can't see your full mapping here, if "Entity" doesn't actually have any meaningful data in it other than to serve as "the base of all objects", IMHO that's an antipattern as far as the relational DB is concerned. Having base classes in Python is fine but I'd definitely not have a database table corresponding to it, it just makes queries and updates much more difficult.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top