diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index 960071fa..f65b331e 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -109,11 +109,8 @@ class QueryResource(BaseResource): for field in ['id', 'created_at', 'api_key', 'visualizations', 'latest_query_data', 'user', 'last_modified_by', 'org']: query_def.pop(field, None) - if 'latest_query_data_id' in query_def: - query_def['latest_query_data'] = query_def.pop('latest_query_data_id') - - if 'data_source_id' in query_def: - query_def['data_source'] = query_def.pop('data_source_id') + if 'query' in query_def: + query_def['query_text'] = query_def.pop('query') query_def['last_modified_by'] = self.current_user query_def['changed_by'] = self.current_user diff --git a/redash/models.py b/redash/models.py index d29917dc..e13978ad 100644 --- a/redash/models.py +++ b/redash/models.py @@ -72,8 +72,10 @@ class GFKBase(object): class PseudoJSON(TypeDecorator): impl = db.Text + def process_bind_param(self, value, dialect): return json_dumps(value) + def process_result_value(self, value, dialect): if not value: return value @@ -120,15 +122,12 @@ class ChangeTrackingMixin(object): col, = attr.columns if attr.key not in self.skipped_fields: changes[col.name] = {'previous': self._clean_values[col.name], - 'current': getattr(self, attr.key)} + 'current': getattr(self, attr.key)} + db.session.add(Change(object=self, - object_version=self.version, - user=changed_by, - change=changes)) - - -class ConflictDetectedError(Exception): - pass + object_version=self.version, + user=changed_by, + change=changes)) class BelongsToOrgMixin(object): @@ -645,7 +644,7 @@ class Query(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model): def to_dict(self, with_stats=False, with_visualizations=False, with_user=True, with_last_modified_by=True): d = { 'id': self.id, - 'latest_query_data_id': self.latest_query_data, + 'latest_query_data_id': self.latest_query_data_id, 'name': self.name, 'description': self.description, 'query': self.query_text, @@ -831,14 +830,17 @@ class Query(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model): def __unicode__(self): return unicode(self.id) + @listens_for(Query.query_text, 'set') def gen_query_hash(target, val, oldval, initiator): target.query_hash = utils.gen_query_hash(val) + @listens_for(Query.user_id, 'set') def query_last_modified_by(target, val, oldval, initiator): target.last_modified_by_id = val + # Create default (table) visualization: @listens_for(SignallingSession, 'before_flush') def create_defaults(session, ctx, *a): @@ -1056,7 +1058,8 @@ class Dashboard(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model name = Column(db.String(100)) user_id = Column(db.Integer, db.ForeignKey("users.id")) user = db.relationship(User) - # XXX replace with association table + # TODO: The layout should dynamically be built from position and size information on each widget. + # Will require update in the frontend code to support this. layout = Column(db.Text) dashboard_filters_enabled = Column(db.Boolean, default=False) is_archived = Column(db.Boolean, default=False, index=True) diff --git a/tests/handlers/test_queries.py b/tests/handlers/test_queries.py index 368eb41b..e5cfc2b1 100644 --- a/tests/handlers/test_queries.py +++ b/tests/handlers/test_queries.py @@ -55,10 +55,23 @@ class TestQueryResourcePost(BaseTestCase): admin = self.factory.create_admin() query = self.factory.create_query() - rv = self.make_request('post', '/api/queries/{0}'.format(query.id), data={'name': 'Testing'}, user=admin) + new_ds = self.factory.create_data_source() + new_qr = self.factory.create_query_result() + + data = { + 'name': 'Testing', + 'query': 'select 2', + 'latest_query_data_id': new_qr.id, + 'data_source_id': new_ds.id + } + + rv = self.make_request('post', '/api/queries/{0}'.format(query.id), data=data, user=admin) self.assertEqual(rv.status_code, 200) - self.assertEqual(rv.json['name'], 'Testing') + self.assertEqual(rv.json['name'], data['name']) self.assertEqual(rv.json['last_modified_by']['id'], admin.id) + self.assertEqual(rv.json['query'], data['query']) + self.assertEqual(rv.json['data_source_id'], data['data_source_id']) + self.assertEqual(rv.json['latest_query_data_id'], data['latest_query_data_id']) def test_raises_error_in_case_of_conflict(self): q = self.factory.create_query()