root/trunk/camelot/view/proxy/queryproxy.py @ 1186

Revision 1186, 7.2 KB (checked in by erikj, 6 months ago)

prepare expanded search and sorting of query proxy models

Line 
1#  ============================================================================
2#
3#  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.
4#  www.conceptive.be / project-camelot@conceptive.be
5#
6#  This file is part of the Camelot Library.
7#
8#  This file may be used under the terms of the GNU General Public
9#  License version 2.0 as published by the Free Software Foundation
10#  and appearing in the file LICENSE.GPL included in the packaging of
11#  this file.  Please review the following information to ensure GNU
12#  General Public Licensing requirements will be met:
13#  http://www.trolltech.com/products/qt/opensource.html
14#
15#  If you are unsure which license is appropriate for your use, please
16#  review the following information:
17#  http://www.trolltech.com/products/qt/licensing.html or contact
18#  project-camelot@conceptive.be.
19#
20#  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21#  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22#
23#  For use of this library in commercial applications, please contact
24#  project-camelot@conceptive.be
25#
26#  ============================================================================
27
28"""Proxies representing the results of a query"""
29
30from PyQt4.QtCore import Qt
31
32import logging
33logger = logging.getLogger('camelot.view.proxy.queryproxy')
34
35from collection_proxy import CollectionProxy, strip_data_from_object
36from camelot.view.model_thread import model_function, gui_function, post
37
38
39class QueryTableProxy(CollectionProxy):
40    """The QueryTableProxy contains a limited copy of the data in the Elixir
41    model, which is fetched from the database to be used as the model for a
42    QTableView
43    """
44
45    def __init__(self, admin, query_getter, columns_getter,
46                 max_number_of_rows=10, edits=None):
47        """@param query_getter: a model_thread function that returns a query"""
48        logger.debug('initialize query table')
49        self._query_getter = query_getter
50        self._sort_decorator = None
51        #rows appended to the table which have not yet been flushed to the
52        #database, and as such cannot be a result of the query
53        self._appended_rows = []
54        CollectionProxy.__init__(self, admin, lambda: [],
55                                 columns_getter, max_number_of_rows=10, edits=None)
56
57    def get_query_getter(self):
58        if not self._sort_decorator:
59            return self._query_getter
60        else:
61           
62            def sorted_query_getter():
63                return self._sort_decorator(self._query_getter())
64           
65            return sorted_query_getter
66   
67    @model_function
68    def _clean_appended_rows(self):
69        """Remove those rows from appended rows that have been flushed"""
70        flushed_rows = []
71        for o in self._appended_rows:
72            if o.id:
73                flushed_rows.append(o)
74        for o in flushed_rows:
75            self._appended_rows.remove(o)
76
77    @model_function
78    def getRowCount(self):
79        self._clean_appended_rows()
80        return self.get_query_getter()().count() + len(self._appended_rows)
81
82    @gui_function
83    def setQuery(self, query_getter):
84        """Set the query and refresh the view"""
85        self._query_getter = query_getter
86        self.refresh()
87       
88    def get_collection_getter(self):
89       
90        def collection_getter():
91            return self.get_query_getter()().all()
92       
93        return collection_getter
94   
95    @gui_function
96    def sort( self, column, order ):
97       
98        def create_set_sort_decorator(column, order):
99
100            def set_sort_decorator():
101                from sqlalchemy import orm
102                from sqlalchemy.exceptions import InvalidRequestError
103                field_name = self._columns[column][0]
104                class_attribute = getattr(self.admin.entity, field_name)
105                mapper = orm.class_mapper(self.admin.entity)
106                try:
107                    property = mapper.get_property(
108                        field_name,
109                        resolve_synonyms=True
110                    )
111                except InvalidRequestError:
112                    #
113                    # If the field name is not a property of the mapper, we cannot
114                    # sort it using sql
115                    #
116                    return self.rows
117               
118                def create_sort_decorator(class_attribute, order):
119                   
120                    def sort_decorator(query):
121                        if order:
122                            return query.order_by(class_attribute.desc())
123                        else:
124                            return query.order_by(class_attribute)
125                   
126                    return sort_decorator
127               
128               
129                self._sort_decorator = create_sort_decorator(class_attribute, order)
130                return self.rows
131                   
132            return set_sort_decorator
133           
134        post( create_set_sort_decorator(column, order), self._refresh_content )
135
136    def append(self, o):
137        """Add an object to this collection, used when inserting a new
138        row, overwrite this method for specific behaviour in subclasses"""
139        if not o.id:
140            self._appended_rows.append(o)
141        self.rows = self.rows + 1
142
143    def remove(self, o):
144        if o in self._appended_rows:
145            self._appended_rows.remove(o)
146        self.rows = self.rows - 1
147
148    @model_function
149    def getData(self):
150        """Generator for all the data queried by this proxy"""
151        for _i,o in enumerate(self.get_query_getter()().all()):
152            yield strip_data_from_object(o, self.getColumns())
153
154    @model_function
155    def _extend_cache(self, offset, limit):
156        """Extend the cache around row"""
157        q = self.get_query_getter()().offset(offset).limit(limit)
158        columns = self.getColumns()
159        for i, o in enumerate(q.all()):
160            self._add_data(columns, i+offset, o)
161        rows_in_query = (self.rows - len(self._appended_rows))
162        # Verify if rows that have not yet been flushed have been requested
163        if offset+limit>=rows_in_query:
164            for row in range(max(rows_in_query,offset), min(offset+limit, self.rows)):
165                o = self._get_object(row)
166                self._add_data(columns, row, o)
167        return (offset, limit)
168
169    @model_function
170    def _get_object(self, row):
171        """Get the object corresponding to row"""
172        if self.rows > 0:
173            self._clean_appended_rows()
174            rows_in_query = (self.rows - len(self._appended_rows))
175            if row >= rows_in_query:
176                return self._appended_rows[row - rows_in_query]
177            # first try to get the primary key out of the cache, if it's not
178            # there, query the collection_getter
179            try:
180                return self.cache[Qt.EditRole].get_entity_at_row(row)
181            except KeyError:
182                pass
183            # momentary hack for list error that prevents forms to be closed
184            res = self.get_query_getter()().offset(row)
185            if isinstance(res, list):
186                res = res[0]
187            # @todo: remove this try catch and find out why it sometimes fails
188            try:
189                return res.limit(1).first()
190            except:
191                pass
Note: See TracBrowser for help on using the browser.