Use JQuery for dynamic lookups of form fields instead of using a large <select> list
This commit is contained in:
parent
816e770ccf
commit
c2195f22ae
11 changed files with 219 additions and 7 deletions
|
@ -1,7 +1,47 @@
|
|||
from django.forms import ModelForm
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.forms import ModelForm, Field
|
||||
from django.forms.widgets import Select, Media
|
||||
from .models.profiles import Team
|
||||
from .models.events import Event
|
||||
|
||||
class LookupMedia(Media):
|
||||
def render(self):
|
||||
return mark_safe('''<script type="text/javascript"><script>
|
||||
$(document).ready(function(){
|
||||
$("#{{ widget.name }}_search").keyup(function() {
|
||||
var searchText = this.value;
|
||||
$.getJSON("{{ widget.source }}?q="+searchText, function(data) {
|
||||
var selectField = $("#{{ widget.name }}_select");
|
||||
selectField.empty();
|
||||
$.each(data, function(){
|
||||
selectField.append('<option value="'+ this.{{ widget.key }} +'">'+ this.{{ widget.label }} + '</option>')
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>''')
|
||||
|
||||
class Lookup(Select):
|
||||
input_type = 'select'
|
||||
template_name = 'forms/widgets/lookup.html'
|
||||
add_id_index = False
|
||||
checked_attribute = {'selected': True}
|
||||
option_inherits_attrs = False
|
||||
|
||||
def __init__(self, source='#', key="id", label="name", attrs=None):
|
||||
super().__init__(attrs)
|
||||
self.source = source
|
||||
self.key = key
|
||||
self.label = label
|
||||
self.name = 'place'
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
context['widget']['source'] = self.source
|
||||
context['widget']['key'] = self.key
|
||||
context['widget']['label'] = self.label
|
||||
return context
|
||||
|
||||
class TeamForm(ModelForm):
|
||||
class Meta:
|
||||
model = Team
|
||||
|
@ -11,14 +51,28 @@ class NewTeamForm(ModelForm):
|
|||
class Meta:
|
||||
model = Team
|
||||
fields = ['name', 'country', 'spr', 'city', 'web_url', 'tz']
|
||||
widgets = {
|
||||
'country': Lookup(source='/api/country/', label='name'),
|
||||
'spr': Lookup(source='/api/spr/', label='name'),
|
||||
'city': Lookup(source='/api/cities/', label='name'),
|
||||
}
|
||||
|
||||
class TeamEventForm(ModelForm):
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ['name', 'start_time', 'end_time', 'summary', 'place', 'web_url', 'announce_url', 'tags']
|
||||
widgets = {
|
||||
'country': Lookup(source='/api/country/', label='name'),
|
||||
'spr': Lookup(source='/api/spr/', label='name'),
|
||||
'city': Lookup(source='/api/cities/', label='name'),
|
||||
}
|
||||
|
||||
class NewTeamEventForm(ModelForm):
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ['name', 'start_time', 'end_time', 'summary', 'place']
|
||||
widgets = {
|
||||
'place': Lookup(source='/api/places/', label='name'),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ from django.contrib.auth.models import User, Group
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.shortcuts import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from .locale import *
|
||||
from .profiles import *
|
||||
from .search import *
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
import pytz
|
||||
|
||||
class Language(models.Model):
|
||||
|
@ -41,6 +43,15 @@ class Country(models.Model):
|
|||
else:
|
||||
return 'no_country'
|
||||
|
||||
class CountrySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Country
|
||||
fields = (
|
||||
'id',
|
||||
'name',
|
||||
'code'
|
||||
)
|
||||
|
||||
class SPR(models.Model):
|
||||
name = models.CharField(_("Name"), max_length=100)
|
||||
code = models.CharField(_("Admin Code"), max_length=8)
|
||||
|
@ -59,6 +70,17 @@ class SPR(models.Model):
|
|||
else:
|
||||
return 'no_spr'
|
||||
|
||||
class SPRSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SPR
|
||||
fields = (
|
||||
'id',
|
||||
'name',
|
||||
'code',
|
||||
'country',
|
||||
'slug'
|
||||
)
|
||||
|
||||
class City(models.Model):
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
|
@ -79,4 +101,14 @@ class City(models.Model):
|
|||
return 'no_city'
|
||||
|
||||
|
||||
class CitySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = City
|
||||
fields = (
|
||||
'id',
|
||||
'name',
|
||||
'spr',
|
||||
'tz',
|
||||
'slug'
|
||||
)
|
||||
|
||||
|
|
5
events/templates/forms/widgets/lookup.html
Normal file
5
events/templates/forms/widgets/lookup.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<select autocomplete="false" id="{{ widget.name }}_select" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} style="width: 300px">
|
||||
<option value="">--------</option>
|
||||
</select>
|
||||
<input autocomplete="false" id="{{ widget.name }}_search" name="{{ widget.name }}_search" />
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
from django.shortcuts import render
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
|
||||
from rest_framework.decorators import api_view, throttle_classes
|
||||
from rest_framework.response import Response
|
||||
|
||||
from .models.search import Searchable, SearchableSerializer
|
||||
from .models.events import Event
|
||||
from .models.events import Event, Place, PlaceSerializer
|
||||
from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer
|
||||
|
||||
import simplejson
|
||||
|
||||
|
@ -18,3 +22,52 @@ def events_list(request, *args, **kwargs):
|
|||
'events_list': events,
|
||||
}
|
||||
return render(request, 'events/event_list.html', context)
|
||||
|
||||
@api_view(['GET'])
|
||||
def places_list(request, *args, **kwargs):
|
||||
places = Place.objects.all()
|
||||
if "q" in request.GET:
|
||||
match = request.GET.get("q", "")
|
||||
places = Place.objects.filter(name__icontains=match)
|
||||
else:
|
||||
places = Place.objects.all()
|
||||
serializer = PlaceSerializer(places, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@api_view(['GET'])
|
||||
def country_list(request, *args, **kwargs):
|
||||
if "q" in request.GET:
|
||||
match = request.GET.get("q", "")
|
||||
countries = Country.objects.filter(name__icontains=match)
|
||||
else:
|
||||
countries = Country.objects.all()
|
||||
serializer = CountrySerializer(countries, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@api_view(['GET'])
|
||||
def spr_list(request, *args, **kwargs):
|
||||
if "q" in request.GET:
|
||||
match = request.GET.get("q", "")
|
||||
sprs = SPR.objects.filter(name__icontains=match)
|
||||
else:
|
||||
sprs = SPR.objects.all()
|
||||
if "country" in request.GET and request.GET.get("country") is not "":
|
||||
sprs = sprs.filter(country=request.GET.get("country"))
|
||||
|
||||
serializer = SPRSerializer(sprs, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@api_view(['GET'])
|
||||
def city_list(request, *args, **kwargs):
|
||||
if "q" in request.GET:
|
||||
match = request.GET.get("q", "")
|
||||
cities = City.objects.filter(name__icontains=match)
|
||||
else:
|
||||
cities = City.objects.all()
|
||||
|
||||
if "spr" in request.GET and request.GET.get("spr") is not "":
|
||||
cities = cities.filter(spr=request.GET.get("spr"))
|
||||
|
||||
serializer = CitySerializer(cities, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
|
|
@ -149,3 +149,4 @@ USE_TZ = True
|
|||
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" integrity="sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy" crossorigin="anonymous" rel="stylesheet">
|
||||
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css" rel="stylesheet">
|
||||
<!-- Bootstrap style overrides -->
|
||||
<style>
|
||||
body {
|
||||
|
@ -60,8 +60,10 @@ body {
|
|||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
|
||||
<script>window.jQuery || document.write('<script src="../../../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script>
|
||||
<script src="../../../../assets/js/vendor/popper.min.js"></script>
|
||||
<script src="../../../../dist/js/bootstrap.min.js"></script> </body>
|
||||
<script src="https://unpkg.com/popper.js@1.12.9/dist/umd/popper.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
|
||||
{% block javascript %}{% endblock %}
|
||||
</html>
|
||||
|
|
|
@ -12,3 +12,21 @@
|
|||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
{% with event_form.fields.place.widget as widget %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$("#{{ widget.name }}_search").keyup(function() {
|
||||
var searchText = this.value;
|
||||
$.getJSON("{{ widget.source }}?q="+searchText, function(data) {
|
||||
var selectField = $("#{{ widget.name }}_select");
|
||||
selectField.empty();
|
||||
$.each(data, function(){
|
||||
selectField.append('<option value="'+ this.{{ widget.key }} +'">'+ this.name+' '+this.city + '</option>')
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,3 +10,42 @@
|
|||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$("#country_search").keyup(function() {
|
||||
var searchText = this.value;
|
||||
$.getJSON("/api/countries/?q="+searchText, function(data) {
|
||||
var selectField = $("#country_select");
|
||||
selectField.empty();
|
||||
$.each(data, function(){
|
||||
selectField.append('<option value="'+ this.id +'">'+ this.name + '</option>')
|
||||
});
|
||||
});
|
||||
});
|
||||
$("#spr_search").keyup(function() {
|
||||
var searchText = this.value;
|
||||
var country_id = $("#country_select")[0].selectedOptions[0].value;
|
||||
$.getJSON("/api/spr/?q="+searchText+"&country="+country_id, function(data) {
|
||||
var selectField = $("#spr_select");
|
||||
selectField.empty();
|
||||
$.each(data, function(){
|
||||
selectField.append('<option value="'+ this.id +'">'+ this.name + '</option>')
|
||||
});
|
||||
});
|
||||
});
|
||||
$("#city_search").keyup(function() {
|
||||
var searchText = this.value;
|
||||
var spr_id = $("#spr_select")[0].selectedOptions[0].value;
|
||||
$.getJSON("/api/cities/?q="+searchText+"&spr="+spr_id, function(data) {
|
||||
var selectField = $("#city_select");
|
||||
selectField.empty();
|
||||
$.each(data, function(){
|
||||
selectField.append('<option value="'+ this.id +'">'+ this.name + '</option>')
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
<center>
|
||||
<h2>Welcome to Get Together!</h2>
|
||||
|
||||
<h3>Login</h3>
|
||||
<h4>Login</h4>
|
||||
<!--
|
||||
<a class="btn btn-primary" href="{% url 'social:begin' 'google-oauth2' %}">Google</a>
|
||||
<a class="btn btn-primary" href="{% url 'social:begin' 'facebook' %}">Facebook</a><br/><br/>
|
||||
<a class="btn btn-primary" href="{% url 'social:begin' 'twitter' %}">Twitter</a>
|
||||
-->
|
||||
<a class="btn btn-primary" href="{% url 'social:begin' 'github' %}">GitHub</a><br/>
|
||||
<br>
|
||||
</center>
|
||||
|
|
|
@ -22,6 +22,10 @@ urlpatterns = [
|
|||
path('', views.home, name='home'),
|
||||
path('admin/', admin.site.urls),
|
||||
path('searchables/', event_views.searchable_list),
|
||||
path('api/places/', event_views.places_list),
|
||||
path('api/countries/', event_views.country_list),
|
||||
path('api/spr/', event_views.spr_list),
|
||||
path('api/cities/', event_views.city_list),
|
||||
|
||||
path('events/', views.events_list, name='events'),
|
||||
path('create-team/', views.create_team, name='create-team'),
|
||||
|
|
Loading…
Reference in a new issue