clave foránea sqlalchemy genérica (como en django ORM)

¿Tiene sqlalchemy algo como GenericForeignKey de django? Y es correcto usar campos generics foráneos.

Mi problema es: tengo varios modelos (por ejemplo, Publicación, Proyecto, Vacante, nada especial allí) y quiero agregar comentarios a cada uno de ellos. Y quiero usar solo un modelo de comentario. ¿Vale la pena hacerlo? ¿O debería usar PostComment, ProjectComment, etc.? ¿Pros / contras de ambas maneras?

¡Gracias!

Related of "clave foránea sqlalchemy genérica (como en django ORM)"

El patrón más simple que uso con más frecuencia es que en realidad tiene tablas de comentarios separadas para cada relación. Esto puede parecer aterrador al principio, pero no incurre en ningún código adicional en comparación con el uso de cualquier otro enfoque: las tablas se crean automáticamente y se hace referencia a los modelos mediante el patrón Post.Comment , Project.Comment , etc. La definición de El comentario se mantiene en un solo lugar. Este enfoque desde un punto de vista referencial es el más simple y eficiente, así como el más compatible con DBA, ya que los diferentes tipos de comentarios se guardan en sus propias tablas, que se pueden clasificar individualmente.

Otro patrón a utilizar es una única tabla de comentarios, pero distintas tablas de asociación. Este patrón ofrece el caso de uso en el que es posible que desee un comentario vinculado a más de un tipo de objeto a la vez (como una publicación y un proyecto al mismo tiempo). Este patrón sigue siendo razonablemente eficiente.

En tercer lugar, está la tabla de asociación polimórfica. Este patrón utiliza un número fijo de tablas para representar las colecciones y la clase relacionada sin sacrificar la integridad referencial. Este patrón trata de acercarse lo más posible a la “clave externa genérica” ​​del estilo Django mientras mantiene la integridad referencial, aunque no es tan simple como los dos enfoques anteriores.

También es posible imitar el patrón usado por ROR / Django, donde no se usan claves externas reales y las filas se combinan con la lógica de la aplicación.

Los primeros tres patrones se ilustran en forma moderna en la distribución SQLAlchemy en ejemplos / generic_associations /.

El patrón ROR / Django, ya que se lo pregunta con tanta frecuencia, también agregaré ejemplos de SQLAlchemy, aunque no me gusta mucho. El enfoque que estoy usando no es exactamente el mismo que hace Django, ya que parecen usar una tabla de “tipos de contenido” para hacer un seguimiento de los tipos, eso me parece superfluo, pero la idea general de una columna de enteros que apunta a cualquier número de tablas basadas en una columna discriminadora presente. Aquí está:

 from sqlalchemy.ext.declarative import declarative_base, declared_attr from sqlalchemy import create_engine, Integer, Column, \ String, and_ from sqlalchemy.orm import Session, relationship, foreign, remote, backref from sqlalchemy import event class Base(object): """Base class which provides automated table name and surrogate primary key column. """ @declared_attr def __tablename__(cls): return cls.__name__.lower() id = Column(Integer, primary_key=True) Base = declarative_base(cls=Base) class Address(Base): """The Address class. This represents all address records in a single table. """ street = Column(String) city = Column(String) zip = Column(String) discriminator = Column(String) """Refers to the type of parent.""" parent_id = Column(Integer) """Refers to the primary key of the parent. This could refer to any table. """ @property def parent(self): """Provides in-Python access to the "parent" by choosing the appropriate relationship. """ return getattr(self, "parent_%s" % self.discriminator) def __repr__(self): return "%s(street=%r, city=%r, zip=%r)" % \ (self.__class__.__name__, self.street, self.city, self.zip) class HasAddresses(object): """HasAddresses mixin, creates a relationship to the address_association table for each parent. """ @event.listens_for(HasAddresses, "mapper_configured", propagate=True) def setup_listener(mapper, class_): name = class_.__name__ discriminator = name.lower() class_.addresses = relationship(Address, primaryjoin=and_( class_.id == foreign(remote(Address.parent_id)), Address.discriminator == discriminator ), backref=backref( "parent_%s" % discriminator, primaryjoin=remote(class_.id) == foreign(Address.parent_id) ) ) @event.listens_for(class_.addresses, "append") def append_address(target, value, initiator): value.discriminator = discriminator class Customer(HasAddresses, Base): name = Column(String) class Supplier(HasAddresses, Base): company_name = Column(String) engine = create_engine('sqlite://', echo=True) Base.metadata.create_all(engine) session = Session(engine) session.add_all([ Customer( name='customer 1', addresses=[ Address( street='123 anywhere street', city="New York", zip="10110"), Address( street='40 main street', city="San Francisco", zip="95732") ] ), Supplier( company_name="Ace Hammers", addresses=[ Address( street='2569 west elm', city="Detroit", zip="56785") ] ), ]) session.commit() for customer in session.query(Customer): for address in customer.addresses: print(address) print(address.parent) 

Sé que esta es probablemente una manera terrible de hacer esto, pero fue una solución rápida para mí.

 class GenericRelation(object): def __init__(self, object_id, object_type): self.object_id = object_id self.object_type = object_type def __composite_values__(self): return (self.object_id, self.object_type) class Permission(AbstractBase): #__abstract__ = True _object = None _generic = composite( GenericRelation, sql.Column('object_id', data_types.UUID, nullable=False), sql.Column('object_type', sql.String, nullable=False), ) permission_type = sql.Column(sql.Integer) @property def object(self): session = object_session(self) if self._object or not session: return self._object else: object_class = eval(self.object_type) self._object = session.query(object_class).filter(object_class.id == self.object_id).first() return self._object @object.setter def object(self, value): self._object = value self.object_type = value.__class__.__name__ self.object_id = value.id