Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy - can you add custom methods to the query object?

Is there a way to create custom methods to the query object so you can do something like this?

User.query.all_active() 

Where all_active() is essentially .filter(User.is_active == True)

And be able to filter off of it?

User.query.all_active().filter(User.age == 30) 
like image 505
mphuie Avatar asked Apr 10 '13 20:04

mphuie


People also ask

How does the querying work with SQLAlchemy?

Python Flask and SQLAlchemy ORM All SELECT statements generated by SQLAlchemy ORM are constructed by Query object. It provides a generative interface, hence successive calls return a new Query object, a copy of the former with additional criteria and options associated with it.

What does all () do in SQLAlchemy?

all() method. The Query object, when asked to return full entities, will deduplicate entries based on primary key, meaning if the same primary key value would appear in the results more than once, only one object of that primary key would be present.

Should I use SQLAlchemy core or ORM?

If you want to view your data in a more schema-centric view (as used in SQL), use Core. If you have data for which business objects are not needed, use Core. If you view your data as business objects, use ORM. If you are building a quick prototype, use ORM.

Is it worth using SQLAlchemy?

SQLAlchemy is great because it provides a good connection / pooling infrastructure; a good Pythonic query building infrastructure; and then a good ORM infrastructure that is capable of complex queries and mappings (as well as some pretty stone-simple ones).


2 Answers

You can subclass the base Query class to add your own methods:

from sqlalchemy.orm import Query  class MyQuery(Query):    def all_active(self):     return self.filter(User.is_active == True) 

You then tell SQLAlchemy to use this new query class when you create the session (docs here). From your code it looks like you might be using Flask-SQLAlchemy, so you would do it as follows:

db = SQLAlchemy(session_options={'query_cls': MyQuery}) 

Otherwise you would pass the argument directly to the sessionmaker:

sessionmaker(bind=engine, query_cls=MyQuery) 

As of right now, this new query object isn't that interesting because we hardcoded the User class in the method, so it won't work for anything else. A better implementation would use the query's underlying class to determine which filter to apply. This is slightly tricky but can be done as well:

class MyOtherQuery(Query):    def _get_models(self):     """Returns the query's underlying model classes."""     if hasattr(query, 'attr'):       # we are dealing with a subquery       return [query.attr.target_mapper]     else:       return [         d['expr'].class_         for d in query.column_descriptions         if isinstance(d['expr'], Mapper)       ]    def all_active(self):     model_class = self._get_models()[0]     return self.filter(model_class.is_active == True) 

Finally, this new query class won't be used by dynamic relationships (if you have any). To let those also use it, you can pass it as argument when you create the relationship:

users = relationship(..., query_class=MyOtherQuery) 
like image 74
mtth Avatar answered Sep 21 '22 10:09

mtth


this work for me finely

class ParentQuery(Query):     def _get_models(self):              if hasattr(query, 'attr'):             return [query.attr.target_mapper]         else:             return self._mapper_zero().class_      def FilterByCustomer(self):         model_class = self._get_models()         return self.filter(model_class.customerId == int(g.customer.get('customerId')))   class AccountWorkflowModel(db.Model):     query_class = ParentQuery     ................. 
like image 38
Shahram Avatar answered Sep 19 '22 10:09

Shahram