2013-04-10 37 views
17

Có cách nào để tạo các phương thức tùy chỉnh cho đối tượng truy vấn để bạn có thể làm điều gì đó như thế này không?SQLAlchemy - bạn có thể thêm các phương thức tùy chỉnh vào đối tượng truy vấn không?

User.query.all_active() 

đâu all_active() về bản chất là .filter(User.is_active == True)

Và có thể lọc ra khỏi nó?

User.query.all_active().filter(User.age == 30) 
+0

Tôi tự hỏi nếu bất cứ điều gì trong khác [câu hỏi/trả lời] này (http://stackoverflow.com/questions/7604967/ sqlalchemy-build-query-filter-động-từ-dict) có thể được sử dụng cho việc này? (Tuy nhiên, ít phương thức tùy chỉnh hơn ... và nhiều bộ lọc động tùy chỉnh hơn.) – summea

Trả lời

33

Bạn có thể phân lớp cơ sở Query lớp để thêm các phương pháp của riêng bạn:

from sqlalchemy.orm import Query 

class MyQuery(Query): 

    def all_active(self): 
    return self.filter(User.is_active == True) 

Sau đó bạn nói với SQLAlchemy sử dụng lớp truy vấn mới này khi bạn tạo phiên (docs here). Từ mã của bạn có vẻ như bạn có thể sử dụng Flask-SQLAlchemy, vì vậy bạn sẽ làm điều đó như sau:

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

Nếu không, bạn sẽ vượt qua đối số trực tiếp đến sessionmaker:

sessionmaker(bind=engine, query_cls=MyQuery) 

Tính đến ngay bây giờ, đối tượng truy vấn mới này không thú vị bởi vì chúng tôi đã mã hóa cứng lớp User trong phương thức, vì vậy nó sẽ không hoạt động cho bất kỳ thứ gì khác. Triển khai tốt hơn sẽ sử dụng lớp cơ bản của truy vấn để xác định bộ lọc nào cần áp dụng. Điều này hơi phức tạp nhưng cũng có thể được thực hiện:

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) 

Cuối cùng, lớp truy vấn mới này sẽ không được sử dụng bởi các mối quan hệ động (nếu bạn có). Để cho những người cũng sử dụng nó, bạn có thể vượt qua nó như là đối số khi bạn tạo các mối quan hệ:

users = relationship(..., query_class=MyOtherQuery) 
+0

Điều này không hiệu quả đối với tôi. https://gist.github.com/nickretallack/76bcd65cc0305abcd33b –

+1

@NickRetallack Tôi đã có một số thành công với việc tạo ra một lớp BaseModel và khai báo thuộc tính lớp query_class. Tuy nhiên, điều này có vẻ là vấn đề: https://github.com/mitsuhiko/flask-sqlalchemy/blob/fe67c633f2e6d66a01a1670e5fc6649506358d20/flask_sqlalchemy/__init__.py#L737 – justanr

+0

Vì vậy, bạn nói rằng một phiên có thể chỉ hoạt động với một lớp truy vấn. Chúng ta có thể làm tốt hơn không? và cho phép cùng một phiên chọn tự động lớp truy vấn? Tôi có nghĩa là các bảng khác nhau có thể có các lớp truy vấn khác nhau, nhưng tôi có thể muốn sử dụng chúng trong cùng một phiên (ví dụ khi sử dụng chúng trong một giao dịch). – Mahdi

Các vấn đề liên quan