Skip to content

Commit

Permalink
Merge pull request #392 from EverythingMe/feature/post_to_create_a_query
Browse files Browse the repository at this point in the history
Support posting to /queries/new to create a new query.
  • Loading branch information
arikfr committed Mar 18, 2015
2 parents 4f1b3d5 + 85a762b commit b8aefd2
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 13 deletions.
21 changes: 19 additions & 2 deletions redash/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ def format_sql_query():
return sqlparse.format(query, reindent=True, keyword_case='upper')


@app.route('/queries/new', methods=['POST'])
@auth.required
def create_query_route():
query = request.form.get('query', None)
data_source_id = request.form.get('data_source_id', None)

if query is None or data_source_id is None:
abort(400)

query = models.Query.create(name="New Query",
query=query,
data_source=data_source_id,
user=current_user._get_current_object(),
ttl=-1)

return redirect('/queries/{}'.format(query.id), 303)


class BaseResource(Resource):
decorators = [auth.required]

Expand Down Expand Up @@ -326,8 +344,6 @@ def post(self):
query = models.Query(**query_def)
query.save()

query.create_default_visualizations()

return query.to_dict()

@require_permission('view_query')
Expand All @@ -350,6 +366,7 @@ def post(self, query_id):
if 'data_source_id' in query_def:
query_def['data_source'] = query_def.pop('data_source_id')

# TODO: use #save() with #dirty_fields.
models.Query.update_instance(query_id, **query_def)

query = models.Query.get_by_id(query_id)
Expand Down
35 changes: 27 additions & 8 deletions redash/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ class Meta:
def get_by_id(cls, model_id):
return cls.get(cls.id == model_id)

def pre_save(self, created):
# Handler for pre_save operations. Overriding if needed.
pass

def post_save(self, created):
# Handler for post_save operations. Overriding if needed.
pass

def save(self, *args, **kwargs):
pk_value = self._get_pk_value()
created = kwargs.get('force_insert', False) or not bool(pk_value)
self.pre_save(created)
super(BaseModel, self).save(*args, **kwargs)
self.post_save(created)


class PermissionsCheckMixin(object):
def has_permission(self, permission):
Expand Down Expand Up @@ -306,12 +321,6 @@ class Query(BaseModel):
class Meta:
db_table = 'queries'

def create_default_visualizations(self):
table_visualization = Visualization(query=self, name="Table",
description='',
type="TABLE", options="{}")
table_visualization.save()

def to_dict(self, with_stats=False, with_visualizations=False, with_user=True):
d = {
'id': self.id,
Expand Down Expand Up @@ -396,6 +405,7 @@ def search(cls, term):

@classmethod
def recent(cls, user_id):
# TODO: instead of t2 here, we should define table_alias for Query table
return cls.select().where(Event.created_at > peewee.SQL("current_date - 7")).\
join(Event, on=(Query.id == peewee.SQL("t2.object_id::integer"))).\
where(Event.action << ('edit', 'execute', 'edit_name', 'edit_description', 'view_source')).\
Expand All @@ -414,10 +424,19 @@ def update_instance(cls, query_id, **kwargs):
update = cls.update(**kwargs).where(cls.id == query_id)
return update.execute()

def save(self, *args, **kwargs):
def pre_save(self, created):
self.query_hash = utils.gen_query_hash(self.query)
self._set_api_key()
super(Query, self).save(*args, **kwargs)

def post_save(self, created):
if created:
self._create_default_visualizations()

def _create_default_visualizations(self):
table_visualization = Visualization(query=self, name="Table",
description='',
type="TABLE", options="{}")
table_visualization.save()

def _set_api_key(self):
if not self.api_key:
Expand Down
3 changes: 2 additions & 1 deletion tests/test_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ def test_delete_visualization(self):
rv = json_request(c.delete, '/api/visualizations/{0}'.format(visualization.id))

self.assertEquals(rv.status_code, 200)
self.assertEquals(models.Visualization.select().count(), 0)
# =1 because each query has a default table visualization.
self.assertEquals(models.Visualization.select().count(), 1)

def test_update_visualization(self):
visualization = visualization_factory.create()
Expand Down
6 changes: 4 additions & 2 deletions tests/test_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ def test_imports_dashboard_correctly(self):
self.assertEqual(dashboard.widgets.count(),
reduce(lambda s, row: s + len(row), self.dashboard['widgets'], 0))

self.assertEqual(models.Visualization.select().count(), dashboard.widgets.count()-1)
self.assertEqual(models.Query.select().count(), dashboard.widgets.count()-2)
queries_count = models.Query.select().count()

self.assertEqual(models.Visualization.select().count(), dashboard.widgets.count()+queries_count-1)
self.assertEqual(queries_count, dashboard.widgets.count()-2)

def test_imports_updates_existing_models(self):
importer = import_export.Importer(data_source=data_source_factory.create())
Expand Down
4 changes: 4 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ def test_search_by_id_returns_query(self):
self.assertNotIn(q1, queries)
self.assertNotIn(q2, queries)

def test_save_creates_default_visualization(self):
q = query_factory.create()
self.assertEquals(q.visualizations.count(), 1)


class QueryArchiveTest(BaseTestCase):
def setUp(self):
Expand Down

0 comments on commit b8aefd2

Please sign in to comment.