Kevin Makice
Kevin Makice

Reputation: 21

With Flask SQLalchemy, can you filter a query by unbound method?

In my Flask python application, I have several interconnected objects, each with it's own is_active toggle to virtually delete the record. When that happens for one record, I want the logical delete to take with it whatever related objects it has. This is how I want to control whether content is published or not in the application.

Since there are other considerations that could factor in besides the relevant is_active fields, I have a method defined in each of the models that looks something like:

def is_published(self):
    """
        Considers all needed criteria for visibility on the website,
        returning a boolean to reflect current publication status
    """
    if self.is_active and self.member.active and self.published_on:
        return True
    return False

where member is a relationship defined through a foreign key. So essentially, this record should only be visible if (a) it is active, (b) the member who owns it is active, and (c) it has a publication date defined, replacing the initial null value.

Later, when I want to query a list of all of these published records, I can do something like:

articles = ArticleModel.query\
    .join(MemberModel, MemberModel.id==ArticleModel.member_id)\
    .filter(ArticleModel.is_active==True)\
    .filter(ArticleModel.published_on!=None)\
    .filter(MemberModel.active==True)\
    .order_by(desc(ArticleModel.modified_at))\
    .all()

This works, but if I want to change the definition for "published" later, I have to find all of the instances of this criteria and make adjustments. I would much rather reference that model's method (or some other central place for that complex criteria).

I want to know if I can set up a query to do something like this:

articles = ArticleModel.query\
    .join(MemberModel, MemberModel.id==ArticleModel.member_id)\
    .filter(ArticleModel.is_published()==True)\
    .order_by(desc(ArticleModel.modified_at))\
    .all()

Python doesn't like that filter line, returning an error: TypeError: unbound method is_published() must be called with ArticleModel instance as first argument (got nothing instead).

Is there a way to set multiple filters in one place for a particular model and reference that in the query?

Upvotes: 0

Views: 681

Answers (1)

Martin
Martin

Reputation: 1069

Have you tried using a hybrid property (docs and more docs)?

EDIT:
Below is a code example (not a very good use case, though). It adds self.title as a fall back for self.short_title.

class SomeModel(db.Model):
    # init blabla
    ...

    @hybrid_property
    def short_title(self):
        return self._short_title if self._short_title is not None else self.title

    @short_title.expression
    def short_title(self):
        return db.case(
            [(Category._short_title == None, Category.title)],
            else_=Category._short_title
        )

    @short_title.setter
    def short_title(self, short_title):
        self._short_title = short_title

Upvotes: 1

Related Questions