Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saved queries #35

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Add ``'djangoql'`` to ``INSTALLED_APPS`` in your ``settings.py``:
...
]

Include the djangoql URLconf in your project ``urls.py``:

.. code:: python

url(r'^djangoql/', include('djangoql.urls'))


Add it to your Django admin
---------------------------
Expand Down
29 changes: 24 additions & 5 deletions djangoql/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json

from django.conf.urls import url
from django.contrib import messages
from django.contrib import messages, admin
from django.contrib.admin.views.main import ChangeList
from django.core.exceptions import FieldError, ValidationError
from django.forms import Media
Expand All @@ -12,9 +12,11 @@
from .exceptions import DjangoQLError
from .queryset import apply_search
from .schema import DjangoQLSchema
from .models import Query


DJANGOQL_SEARCH_MARKER = 'q-l'
DJANGOQL_QUERY_ID = 'q-id'


class DjangoQLChangeList(ChangeList):
Expand All @@ -25,6 +27,8 @@ def get_filters_params(self, *args, **kwargs):
)
if DJANGOQL_SEARCH_MARKER in params:
del params[DJANGOQL_SEARCH_MARKER]
if DJANGOQL_QUERY_ID in self.params:
del params[DJANGOQL_QUERY_ID]
return params


Expand All @@ -33,6 +37,7 @@ class DjangoQLSearchMixin(object):
djangoql_completion = True
djangoql_schema = DjangoQLSchema
djangoql_syntax_help_template = 'djangoql/syntax_help.html'
djangoql_saved_queries = False

def search_mode_toggle_enabled(self):
# If search fields were defined on a child ModelAdmin instance,
Expand Down Expand Up @@ -75,16 +80,25 @@ def media(self):
if self.djangoql_completion:
js = [
'djangoql/js/lib/lexer.js',
'djangoql/js/helpers.js',
'djangoql/js/completion.js',
]
css = [
'djangoql/css/completion.css',
'djangoql/css/completion_admin.css',
]
if self.search_mode_toggle_enabled():
js.append('djangoql/js/completion_admin_toggle.js')

js.append('djangoql/js/completion_admin.js')

if self.djangoql_saved_queries:
js.append('djangoql/js/saved_queries.js')
js.append('djangoql/js/saved_queries_admin.js')
css.append('djangoql/css/saved_queries.css')

media += Media(
css={'': (
'djangoql/css/completion.css',
'djangoql/css/completion_admin.css',
)},
css={'': css},
js=js,
)
return media
Expand Down Expand Up @@ -117,3 +131,8 @@ def introspect(self, request):
content=json.dumps(response, indent=2),
content_type='application/json; charset=utf-8',
)


@admin.register(Query)
class SavedQueryAdmin(admin.ModelAdmin):
pass
8 changes: 8 additions & 0 deletions djangoql/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django import forms
from .models import Query


class QueryForm(forms.ModelForm):
class Meta:
model = Query
fields = ['name', 'text', 'private']
29 changes: 29 additions & 0 deletions djangoql/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 2.1.5 on 2019-02-11 18:47

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Query',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('text', models.TextField()),
('private', models.BooleanField(default=True)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='favorite_query_list', to=settings.AUTH_USER_MODEL)),
],
),
]
Empty file added djangoql/migrations/__init__.py
Empty file.
20 changes: 20 additions & 0 deletions djangoql/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.db import models
from django.conf import settings


class Query(models.Model):
name = models.CharField(max_length=100)
text = models.TextField()
private = models.BooleanField(default=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='favorite_query_list',
on_delete=models.CASCADE,
blank=True,
null=True)
content_type = models.ForeignKey(
'contenttypes.ContentType',
on_delete=models.CASCADE)

def __str__(self):
return self.text
8 changes: 7 additions & 1 deletion djangoql/static/djangoql/css/completion_admin.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#changelist #toolbar form textarea#searchbar {
width: 80%;
flex-grow: 1;
margin-right: 5px;
}

#changelist-search div {
display: flex;
align-items: baseline;
}

.djangoql-toggle {
Expand Down
131 changes: 131 additions & 0 deletions djangoql/static/djangoql/css/saved_queries.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
.djangoql-sq-dialog {
position: absolute;
overflow: hidden;
width: 350px;
display: none;
border: solid 1px #ccc;
border-radius: 4px;
background: white;
min-width: 183px;
font-size: 13px;
}

.djangoql-sq-dialog ul {
padding: 2px 0;
margin: 0;
max-height: 295px;
overflow: auto;
}

.djangoql-sq-dialog li {
list-style: none;
padding: 4px 10px;
cursor: pointer;
}

.djangoql-sq-button {
margin: 0 5px !important;
}

.djangoql-sq-actions li:last-child {
border-bottom: 1px solid #ccc;
padding-bottom: 8px;
}

.djangoql-sq-actions li {
color: #447e9b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.djangoql-sq-actions li:hover {
color: #036;
}

.djangoql-sq-label {
font-size: 12px;
color: #666;
display: none;
margin-top: 10px;
}


#changelist-search .djangoql-sq-label {
margin-left: 22px;
}

.djangoql-sq-query-row {
display: flex;
overflow: hidden;
}

.djangoql-sq-query-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
cursor: pointer;
}

.djangoql-sq-query-name:hover {
color: #000;
}

.djangoql-sq-ops {
display: none;
margin-left: 5px;
}

.djangoql-sq-query-row:hover .djangoql-sq-ops {
display: block;
white-space: nowrap;
}

.djangoql-sq-label-reset {
width: 8px;
height: 8px;
background: url(../img/remove.svg);
background-size: 100%;
}

.djangoql-sq-ops span, .djangoql-sq-label-reset {
display: inline-block;
margin: 0 3px;
cursor: pointer;

color: #666;
}

.djangoql-sq-ops span:hover {
color: rgba(255, 255, 255, 0.7) !important;
}

.djangoql-sq-label:hover .djangoql-sq-label-reset {
display: inline-block;
}

.django-sq-query-list-placeholder {
color: #999;
background: none !important;
display: none;
}

.django-sq-query-list li:hover {
background-color: #79aec8;
}

.django-sq-query-list li:hover .djangoql-sq-ops span {
color: white;
background-color: #79aec8;
}

.django-sq-query-list li:hover .djangoql-sq-ops span,
.django-sq-query-list li:hover .djangoql-sq-query-name {
color: white;
background-color: #79aec8;
}

.djangoql-sq-button-container {
display: inline-block;
}
4 changes: 4 additions & 0 deletions djangoql/static/djangoql/img/remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 3 additions & 28 deletions djangoql/static/djangoql/js/completion_admin.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
(function (DjangoQL) {
(function (DjangoQL, helpers) {
'use strict';

function parseQueryString() {
var qs = window.location.search.substring(1);
var result = {};
var vars = qs.split('&');
var i;
var l = vars.length;
var pair;
var key;
for (i = 0; i < l; i++) {
pair = vars[i].split('=');
key = decodeURIComponent(pair[0]);
if (key) {
if (typeof result[key] !== 'undefined') {
if (({}).toString.call(result[key]) !== '[object Array]') {
result[key] = [result[key]];
}
result[key].push(decodeURIComponent(pair[1]));
} else {
result[key] = decodeURIComponent(pair[1]);
}
}
}
return result;
}

// Replace standard search input with textarea and add completion toggle
DjangoQL.DOMReady(function () {
// use '-' in the param name to prevent conflicts with any model field name
var QLParamName = 'q-l';
var QLEnabled = parseQueryString()[QLParamName] === 'on';
var QLEnabled = helpers.parseQueryString()[QLParamName] === 'on';
var QLInput;
var QLToggle;
var QLPlaceholder = 'Advanced search with Query Language';
Expand Down Expand Up @@ -100,4 +75,4 @@
autoResize: true
});
});
}(window.DjangoQL));
}(window.DjangoQL, window.DjangoQLHelpers));
Loading