Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
luxcem
memopol
Commits
1125cbf6
Commit
1125cbf6
authored
Oct 13, 2016
by
Nicolas Joyard
Browse files
Add autocomplete to search forms
parent
c334858e
Changes
8
Hide whitespace changes
Inline
Side-by-side
setup.py
View file @
1125cbf6
...
...
@@ -11,7 +11,7 @@ setup(name='political-memory',
author_email
=
'cortex@worlddomination.be'
,
url
=
'http://github.com/political-memory/political_memory/'
,
install_requires
=
[
'django-autocomplete-light==3.
1.6
'
,
'django-autocomplete-light==3.
2.0
'
,
'django-autoslug>=1.9,<1.10'
,
'django-bootstrap3>=6,<7'
,
'django-coffeescript>=0.7,<0.8'
,
...
...
src/memopol/context_processors.py
View file @
1125cbf6
from
.forms
import
DossierSearchForm
,
RepresentativeSearchForm
from
memopol_settings.models
import
Setting
from
representatives.models
import
Chamber
,
Group
def
search_form_options
(
request
):
d
=
{}
# Note: Those queries needs to be eval in the template so that we can cache
# it efficiently
d
[
'chambers'
]
=
Chamber
.
objects
.
all
()
d
[
'countries'
]
=
Group
.
objects
.
filter
(
kind
=
'country'
)
d
[
'parties'
]
=
Group
.
objects
.
filter
(
kind
=
'group'
)
d
[
'delegations'
]
=
Group
.
objects
.
filter
(
kind
=
'delegation'
)
d
[
'committees'
]
=
Group
.
objects
.
filter
(
kind
=
'committee'
)
return
d
def
search_forms
(
request
):
return
{
'representative_search_form'
:
RepresentativeSearchForm
(
request
.
GET
),
'dossier_search_form'
:
DossierSearchForm
(
request
.
GET
)
}
def
intro_text
(
request
):
...
...
src/memopol/forms.py
0 → 100644
View file @
1125cbf6
from
django
import
forms
from
dal
import
autocomplete
,
forward
from
representatives.models
import
Chamber
,
Group
,
Representative
from
representatives_votes.models
import
Dossier
class
RepresentativeSearchForm
(
forms
.
Form
):
search
=
forms
.
CharField
(
required
=
False
,
label
=
'Name'
,
widget
=
forms
.
TextInput
(
attrs
=
{
'placeholder'
:
''
})
)
scoremin
=
forms
.
FloatField
(
required
=
False
,
label
=
'Between'
,
widget
=
forms
.
NumberInput
(
attrs
=
{
'placeholder'
:
'Min. score'
})
)
scoremax
=
forms
.
FloatField
(
required
=
False
,
label
=
'and'
,
widget
=
forms
.
NumberInput
(
attrs
=
{
'placeholder'
:
'Max. score'
})
)
chamber
=
forms
.
ModelChoiceField
(
queryset
=
Chamber
.
objects
.
all
(),
required
=
False
,
widget
=
autocomplete
.
ModelSelect2
(
url
=
'chamber-autocomplete'
,
)
)
country
=
forms
.
ModelChoiceField
(
queryset
=
Group
.
objects
.
filter
(
kind
=
'country'
),
required
=
False
,
widget
=
autocomplete
.
ModelSelect2
(
url
=
'group-autocomplete'
,
forward
=
(
forward
.
Const
(
'country'
,
'kind'
),)
)
)
party
=
forms
.
ModelChoiceField
(
queryset
=
Group
.
objects
.
filter
(
kind
=
'group'
),
required
=
False
,
widget
=
autocomplete
.
ModelSelect2
(
url
=
'group-autocomplete'
,
forward
=
(
forward
.
Const
(
'group'
,
'kind'
),)
)
)
committee
=
forms
.
ModelChoiceField
(
queryset
=
Group
.
objects
.
filter
(
kind
=
'committee'
),
required
=
False
,
widget
=
autocomplete
.
ModelSelect2
(
url
=
'group-autocomplete'
,
forward
=
(
forward
.
Const
(
'committee'
,
'kind'
),)
)
)
delegation
=
forms
.
ModelChoiceField
(
queryset
=
Group
.
objects
.
filter
(
kind
=
'delegation'
),
required
=
False
,
widget
=
autocomplete
.
ModelSelect2
(
url
=
'group-autocomplete'
,
forward
=
(
forward
.
Const
(
'delegation'
,
'kind'
),)
)
)
class
DossierSearchForm
(
forms
.
Form
):
search
=
forms
.
CharField
(
required
=
False
,
label
=
'Name'
,
widget
=
forms
.
TextInput
(
attrs
=
{
'placeholder'
:
''
})
)
chamber
=
forms
.
ModelChoiceField
(
queryset
=
Chamber
.
objects
.
all
(),
required
=
False
,
widget
=
autocomplete
.
ModelSelect2
(
url
=
'chamber-autocomplete'
,
)
)
src/memopol/settings.py
View file @
1125cbf6
...
...
@@ -133,7 +133,7 @@ TEMPLATE_LOADERS = (
TEMPLATE_CONTEXT_PROCESSORS
=
global_settings
.
TEMPLATE_CONTEXT_PROCESSORS
+
(
'django.template.context_processors.request'
,
'memopol.context_processors.search_form
_option
s'
,
'memopol.context_processors.search_forms'
,
'memopol.context_processors.intro_text'
)
...
...
src/memopol/static/css/custom.css
View file @
1125cbf6
...
...
@@ -99,7 +99,7 @@ label {
font-weight
:
400
;
}
.form-control
,
input
,
textarea
{
.form-control
,
.control-label
,
input
,
textarea
{
color
:
#212121
!important
;
}
...
...
@@ -129,7 +129,7 @@ label {
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.075
),
0
0
8px
rgba
(
102
,
175
,
233
,
0.6
);
}
textarea
,
textarea
.form-control
,
input
.form-control
,
input
[
type
=
text
],
input
[
type
=
password
],
input
[
type
=
email
],
input
[
type
=
number
],
[
type
=
text
]
.form-control
,
[
type
=
password
]
.form-control
,
[
type
=
email
]
.form-control
,
[
type
=
tel
]
.form-control
,
[
contenteditable
]
.form-control
{
textarea
,
textarea
.form-control
,
input
.form-control
,
input
[
type
=
text
],
input
[
type
=
number
],
input
[
type
=
password
],
input
[
type
=
email
],
input
[
type
=
number
],
[
type
=
text
]
.form-control
,
[
type
=
number
]
.form-control
,
[
type
=
password
]
.form-control
,
[
type
=
email
]
.form-control
,
[
type
=
tel
]
.form-control
,
[
contenteditable
]
.form-control
{
padding
:
0
;
border
:
none
;
border-radius
:
0
;
...
...
@@ -138,7 +138,7 @@ textarea, textarea.form-control, input.form-control, input[type=text], input[typ
box-shadow
:
inset
0
-1px
0
#ddd
;
}
textarea
:focus
,
textarea
.form-control
:focus
,
input
.form-control
:focus
,
input
[
type
=
text
]
:focus
,
input
[
type
=
password
]
:focus
,
input
[
type
=
email
]
:focus
,
input
[
type
=
number
]
:focus
,
[
type
=
text
]
.form-control
:focus
,
[
type
=
password
]
.form-control
:focus
,
[
type
=
email
]
.form-control
:focus
,
[
type
=
tel
]
.form-control
:focus
,
[
contenteditable
]
.form-control
:focus
{
textarea
:focus
,
textarea
.form-control
:focus
,
input
.form-control
:focus
,
input
[
type
=
text
]
:focus
,
input
[
type
=
number
]
:focus
,
input
[
type
=
password
]
:focus
,
input
[
type
=
email
]
:focus
,
input
[
type
=
number
]
:focus
,
[
type
=
text
]
.form-control
:focus
,
[
type
=
number
]
.form-control
:focus
,
[
type
=
password
]
.form-control
:focus
,
[
type
=
email
]
.form-control
:focus
,
[
type
=
tel
]
.form-control
:focus
,
[
contenteditable
]
.form-control
:focus
{
-webkit-box-shadow
:
inset
0
-2px
0
#487ED6
;
box-shadow
:
inset
0
-2px
0
#487ED6
;
}
...
...
src/memopol/templates/_base_search.html
View file @
1125cbf6
{% load i18n memopol_tags %}
{% load fontawesome %}
{% load bootstrap3 %}
<form
class=
"form-horizontal hidden-print"
method=
"GET"
action=
"{% url "
representative-list
"
%}"
>
<div
class=
"input-group"
>
...
...
@@ -30,97 +31,20 @@
aria-expanded=
"{% if view == 'representative_list' and filter.data %}true{% else %}false{% endif %}"
id=
"form-rep"
>
<form
id=
"rep-search-form"
class=
"form-horizontal"
method=
"GET"
action=
"{% url "
representative-list
"
%}"
>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"search-rep"
>
{% trans "Name" %}
</label
>
<div
class=
"col-
sm-9
"
>
<input
type=
"text"
class=
"form-control"
name=
"search"
id=
"search-rep"
value=
"{{ filter.data.search }}"
>
{% bootstrap_field representative_search_form.search layout='horizontal' %}
<div
class=
"row"
>
<div
class=
"col-
md-7
"
>
{% bootstrap_field representative_search_form.scoremin layout='horizontal' horizontal_label_class='col-md-5' horizontal_field_class='col-md-7' placeholder='prout' %}
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"score-min"
>
{% trans "Score between" %}
</label>
<div
class=
"col-sm-4"
>
<input
type=
"number"
class=
"form-control"
name=
"scoremin"
id=
"score-min"
value=
"{{ filter.data.scoremin }}"
>
</div>
<label
class=
"col-sm-1"
for=
"score-max"
>
{% trans "and" %}
</label>
<div
class=
"col-sm-4"
>
<input
type=
"number"
class=
"form-control"
name=
"scoremax"
id=
"score-max"
value=
"{{ filter.data.scoremax }}"
>
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"chamber-rep"
>
{% trans "Chamber" %}
</label>
<div
class=
"col-sm-9"
>
<select
class=
"form-control"
id=
"chamber-rep"
name=
"chamber"
>
<option
value=
""
>
{% trans "All" %}
</option>
{% for chamber in chambers %}
<option
{%
if
filter.data.chamber =
chamber.id|cast_str
%}
selected
{%
endif
%}
value=
"{{ chamber.id }}"
>
{{ chamber.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"country"
>
{% trans "Country" %}
</label>
<div
class=
"col-sm-9"
>
<select
class=
"form-control"
id=
"country"
name=
"country"
>
<option
value=
""
>
{% trans "All" %}
</option>
{% for country in countries %}
<option
{%
if
filter.data.country =
country.id|cast_str
%}
selected
{%
endif
%}
value=
"{{ country.id }}"
>
{% if country.abbreviation %}{{ country.abbreviation }}
–
{% endif %}
{{ country.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"party"
>
{% trans "Party" %}
</label>
<div
class=
"col-sm-9"
>
<select
class=
"form-control"
id=
"party"
name=
"party"
>
<option
value=
""
>
{% trans "All" %}
</option>
{% for party in parties %}
<option
{%
if
filter.data.party =
party.pk|cast_str
%}
selected
{%
endif
%}
value=
"{{ party.pk }}"
>
{% if party.abbreviation %}{{ party.abbreviation }}
–
{% endif %}
{{ party.name }}
</option>
{% endfor %}
</select>
<div
class=
"col-md-5"
>
{% bootstrap_field representative_search_form.scoremax layout='horizontal' horizontal_label_class='col-md-2' horizontal_field_class='col-md-10' %}
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"committee"
>
{% trans "Committee" %}
</label>
<div
class=
"col-sm-9"
>
<select
class=
"form-control"
id=
"committee"
name=
"committee"
>
<option
value=
""
>
{% trans "All" %}
</option>
{% for committee in committees %}
<option
{%
if
filter.data.committee =
committee.pk|cast_str
%}
selected
{%
endif
%}
value=
"{{ committee.pk }}"
data-url=
"{% url "
representative-list
"
%}?
committee=
{{
committee.pk
}}"
>
{% if committee.abbreviation %}{{ committee.abbreviation }}
–
{% endif %}
{{ committee.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"delegation"
>
{% trans "Delegation" %}
</label>
<div
class=
"col-sm-9"
>
<select
class=
"form-control"
id=
"delegation"
name=
"delegation"
>
<option
value=
""
>
{% trans "All" %}
</option>
{% for delegation in delegations %}
<option
{%
if
filter.data.delegation =
delegation.pk|cast_str
%}
selected
{%
endif
%}
value=
"{{ delegation.pk }}"
data-url=
"{% url "
representative-list
"
%}?
delegation=
{{
delegation.pk
}}"
>
{{ delegation.name }}
</option>
{% endfor %}
</select>
</div>
</div>
{% bootstrap_field representative_search_form.chamber layout='horizontal' %}
{% bootstrap_field representative_search_form.country layout='horizontal' %}
{% bootstrap_field representative_search_form.party layout='horizontal' %}
{% bootstrap_field representative_search_form.committee layout='horizontal' %}
{% bootstrap_field representative_search_form.delegation layout='horizontal' %}
<button
type=
"submit"
class=
"btn btn-default"
>
{% trans "Search" %}
</button>
</form>
</div>
...
...
@@ -133,26 +57,8 @@
<div
class=
"collapse{% if view == 'dossier_list' and filter.data %} in{% endif %}"
aria-expanded=
"{% if view == 'dossier_list' and filter.data %}true{% else %}false{% endif %}"
id=
"form-dossier"
>
<form
id=
"dossier-search-form"
class=
"form-horizontal"
method=
"GET"
action=
"{% url "
dossier-list
"
%}"
>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"search-dossier"
>
{% trans "Name" %}
</label>
<div
class=
"col-sm-9"
>
<input
type=
"text"
class=
"form-control"
name=
"search"
id=
"search-dossier"
value=
"{{ filter.data.search }}"
>
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"col-sm-3"
for=
"chamber-dossier"
>
{% trans "Chamber" %}
</label>
<div
class=
"col-sm-9"
>
<select
class=
"form-control"
name=
"chamber"
id=
"chamber-dossier"
>
<option
value=
""
>
{% trans "All" %}
</option>
{% for chamber in chambers %}
<option
{%
if
filter.data.chamber =
chamber.id|cast_str
%}
selected
{%
endif
%}
value=
"{{ chamber.id }}"
>
{{ chamber.name }}
</option>
{% endfor %}
</select>
</div>
</div>
{% bootstrap_field dossier_search_form.search layout='horizontal' %}
{% bootstrap_field dossier_search_form.chamber layout='horizontal' %}
<button
type=
"submit"
class=
"btn btn-default"
>
{% trans "Search" %}
</button>
</form>
...
...
src/memopol/urls.py
View file @
1125cbf6
...
...
@@ -9,6 +9,8 @@ from views.autocomplete import (
ProposalAutocomplete
,
RepresentativeAutocomplete
,
ThemeAutocomplete
,
ChamberAutocomplete
,
GroupAutocomplete
,
)
from
views.charts
import
ThemeScoresJSONView
,
ChamberScoresJSONView
...
...
@@ -146,6 +148,17 @@ urlpatterns = [
name
=
'theme-autocomplete'
,
),
url
(
r
'^autocomplete/chamber/$'
,
ChamberAutocomplete
.
as_view
(),
name
=
'chamber-autocomplete'
,
),
url
(
r
'^autocomplete/group/$'
,
GroupAutocomplete
.
as_view
(),
name
=
'group-autocomplete'
,
),
# Theme list
...
...
src/memopol/views/autocomplete.py
View file @
1125cbf6
...
...
@@ -4,7 +4,7 @@ from dal import autocomplete
from
django.db.models
import
Q
from
representatives.models
import
Representative
from
representatives.models
import
Representative
,
Chamber
,
Group
from
representatives_votes.models
import
Proposal
from
memopol_themes.models
import
Theme
...
...
@@ -43,3 +43,28 @@ class ThemeAutocomplete(autocomplete.Select2QuerySetView):
qs
=
qs
.
filter
(
name__icontains
=
self
.
q
)
return
qs
class
ChamberAutocomplete
(
autocomplete
.
Select2QuerySetView
):
def
get_queryset
(
self
):
qs
=
Chamber
.
objects
.
all
()
if
self
.
q
:
qs
=
qs
.
filter
(
Q
(
name__icontains
=
self
.
q
)
|
Q
(
abbreviation__icontains
=
self
.
q
))
return
qs
class
GroupAutocomplete
(
autocomplete
.
Select2QuerySetView
):
def
get_queryset
(
self
):
qs
=
Group
.
objects
.
all
()
kind
=
self
.
forwarded
.
get
(
'kind'
,
None
)
if
kind
:
qs
=
qs
.
filter
(
kind
=
kind
)
if
self
.
q
:
qs
=
qs
.
filter
(
Q
(
name__icontains
=
self
.
q
))
return
qs
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment