biosustain / potion

Flask-Potion is a RESTful API framework for Flask and SQLAlchemy, Peewee or MongoEngine
http://potion.readthedocs.io
Other
488 stars 51 forks source link

Support for polymorphic relationships #92

Closed Alain1405 closed 8 years ago

Alain1405 commented 8 years ago

As I need to associate some tags (rfid, nfc and so on) to multiple resources, I created a polymorphic association between TagsSet and a resource.

I use the following mixin for extending SqlAlchemy models which have tags:

class HasTags(object):
    """
    SqlAlchemy mixin, creates a relationship to the tags_set_association table for each model which has tags.
    """
    tags_set = None

    @declared_attr
    def tags_set_association_id(self):
        return db.Column(
            db.Integer,
            db.ForeignKey("tags_set_association.id")
        )

    @declared_attr
    def tags_set_association(self):
        name = self.__name__
        parent_type = name.lower()

        assoc_class = type(
            "{0}TagsSetAssociation".format(name),
            (TagsSetAssociation,),
            dict(
                __tablename__=None,
                __mapper_args__={
                    "polymorphic_identity": parent_type
                }
            )
        )

        self.tags_set = association_proxy(
            "tags_set_association", "tags_set",
            creator=lambda tags_set: assoc_class(tags_set=tags_set)
        )
        return db.relationship(assoc_class, backref=backref("parent", uselist=False))

Then I extend a model with the mixin:

class User(HasTags, db.Model):
   ...
class Res1(HasTags, db.Model):
   ...
class Res2(HasTags, db.Model):
   ...

You can see the association uses as discriminator the parent_type. Then I can do something like:

tag_set = TagsSet.query.first()
resource = TagsSet.parent

Where resource can be of any type.

Is there a way to set this up on flask_potion?

class TagsSetResource(MetaMixin, ModelResource):
    class Meta:
        model = TagsSet

    class Schema:
        parent = fields.ToOne('*any*')
lyschoening commented 8 years ago

You have to write your own field class if you want a ToOne that just accepts any reference. It is not difficult to do, but out of scope for the potion package.

Alternatively you can simply make a generic "taggable items" resource that covers all models that can be associated with a tag, and then have a fields.ToOne('taggable').

Alain1405 commented 8 years ago

Ok, I see. When you mean a generic "taggable items" resource you mean that a Potion resource can point to multiple models? Or a SqlAlchemy Model that points to multiple tables?

lyschoening commented 8 years ago

A Potion resource that can point to multiple models, which is easier if you have an SQLAlchemy model that points to multiple tables.