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

List autocompletion #702

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 44 additions & 5 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ and `Mixins <https://en.wikipedia.org/wiki/Mixin>`_ to for code reuse.
<http://ccbv.co.uk/>`_ website which helps a lot to work with
class-based views in general.

In this tutorial, we'll learn to make autocompletes backed by a
:django:term:`QuerySet`. Suppose we have a Country :django:term:`Model`
which we want to provide a `Select2 <https://select2.github.io/>`_ autocomplete
widget for in a form. If a users types an "f" it would propose "Fiji",
"Finland" and "France", to authenticated users only:
In this tutorial, we'll first learn to make autocompletes backed by a
:django:term:`QuerySet`. Suppose we have a Country
:django:term:`Model` which we want to provide a `Select2
<https://select2.github.io/>`_ autocomplete widget for in a form. If a
users types an "f" it would propose "Fiji", "Finland" and "France", to
authenticated users only:

.. image:: img/autocomplete.png

Expand Down Expand Up @@ -429,3 +430,41 @@ You can use the ``$.getFormPrefix()`` jQuery plugin used by DAL to clear the

To autoload the script with the form, you can use `Form.Media
<https://docs.djangoproject.com/en/1.9/topics/forms/media/#media-on-forms>`_.

Autocompleting based on a List of Strings
=========================================

Sometimes it is useful to specify autocomplete choices based on a list
of strings rather than a QuerySet. This can be achieved with the
:py:class:`~dal_select2.views.Select2ListView` class:

.. code-block:: python

from dal import autocomplete

class CountryAutocompleteFromList(autocomplete.Select2ListView):
def get_list(self):
return ['France', 'Fiji', 'Finland', 'Switzerland']

This class can then be registered as in the previous example. Suppose
we register it under URL 'country-list-autocomplete'. We can then a
create a Select2 widget with:

.. code-block:: python
widget = autocomplete.Select2(url='country-list-autocomplete')

With this in place, if a user types the letter ``f``' in the widget, choices
'France', 'Fiji', and 'Finland' would be offered.

Specifying Placeholder Labels
=============================

To display a place-holder text when a widget has no data entered yet,
set the attribute 'data-placeholder' to the desired label when creating
the widget. For example:

.. code-block:: python
widget = autocomplete.Select2(url='country-list-autocomplete',
attrs={'data-placeholder': 'Country?'})

Would show the label ``Country?`` as a placeholder.
6 changes: 5 additions & 1 deletion src/dal/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

from .forms import FutureModelForm

from .views import ViewMixin

from .widgets import (
Select,
SelectMultiple,
Expand All @@ -39,10 +41,12 @@ def _installed(*apps):
Select2Multiple,
ModelSelect2,
ModelSelect2Multiple,
Select2,
TagSelect2,
)
from dal_select2.views import (
Select2QuerySetView
Select2QuerySetView,
Select2ListView
)

if _installed('dal_queryset_sequence'):
Expand Down
42 changes: 41 additions & 1 deletion src/dal_select2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import json

from dal.views import BaseQuerySetView
from dal.views import BaseQuerySetView, ViewMixin

from django import http
from django.utils.translation import ugettext as _
from django.views.generic.list import View, BaseListView


class Select2ViewMixin(object):
Expand Down Expand Up @@ -52,3 +53,42 @@ def render_to_response(self, context):

class Select2QuerySetView(Select2ViewMixin, BaseQuerySetView):
"""List options for a Select2 widget."""


class Select2ListView(ViewMixin, View):
"""Autocomplete from a list of items rather than a QuerySet."""

def get_list(self):
""""Return the list strings from which to autocomplete."""
return []

def get(self, request, *args, **kwargs):
results = self.get_list()
create_option = []
if self.q:
results = [ x for x in results if self.q in x ]
if hasattr(self, 'create'):
create_option = [{
'id': self.q,
'text': 'Create "%s"' % self.q,
'create_id': True
}]
return http.HttpResponse(json.dumps({
'results': [dict(id=x, text=x) for x in results] + create_option
}))

def post(self, request):
text = request.POST.get('text', None)

if text is None:
return http.HttpResponseBadRequest()

text = self.create(text)

if text is None:
return http.HttpResponseBadRequest()

return http.HttpResponse(json.dumps({
'id': text,
'text': text,
}))
4 changes: 4 additions & 0 deletions src/dal_select2/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class Select2Multiple(Select2WidgetMixin, SelectMultiple):
"""Select2Multiple widget for regular choices."""


class ListSelect2(WidgetMixin, Select2WidgetMixin, forms.Select):
"""Select widget for regular choices and Select2."""


class ModelSelect2(QuerySetSelectMixin,
Select2WidgetMixin,
forms.Select):
Expand Down
1 change: 1 addition & 0 deletions test_project/select2_list/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

16 changes: 16 additions & 0 deletions test_project/select2_list/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.contrib import admin

from .forms import TestForm
from .models import TestModel


class TestInline(admin.TabularInline):
form = TestForm
model = TestModel
fk_name = 'for_inline'


class TestAdmin(admin.ModelAdmin):
form = TestForm
inlines = [TestInline]
admin.site.register(TestModel, TestAdmin)
20 changes: 20 additions & 0 deletions test_project/select2_list/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from dal import autocomplete

from django import forms

from .models import TestModel


class TestForm(forms.ModelForm):
test = forms.ChoiceField(
choices=[
('windows', 'windows'),
('linux', 'linux'),
],
required=False,
widget=autocomplete.Select2(url='select2_list')
)

class Meta:
model = TestModel
fields = ('name', 'test')
26 changes: 26 additions & 0 deletions test_project/select2_list/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-03-11 01:22
from __future__ import unicode_literals

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


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='TestModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('test', models.CharField(blank=True, max_length=100, null=True)),
('for_inline', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inline_test_models', to='select2_list.TestModel')),
],
),
]
Empty file.
23 changes: 23 additions & 0 deletions test_project/select2_list/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.db import models
from django.utils.encoding import python_2_unicode_compatible


@python_2_unicode_compatible
class TestModel(models.Model):
name = models.CharField(max_length=200)

test = models.CharField(
max_length=100,
null=True,
blank=True
)

for_inline = models.ForeignKey(
'self',
null=True,
blank=True,
related_name='inline_test_models'
)

def __str__(self):
return self.name
29 changes: 29 additions & 0 deletions test_project/select2_list/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import json

from dal import autocomplete

from django.conf.urls import url
from django import http
from django.views import generic


class YourListView(autocomplete.ViewMixin, generic.View):
def get(self, request, *args, **kwargs):
results = ['windows', 'linux']

if self.q:
results = [
x for x in results if self.q in x
]
return http.HttpResponse(json.dumps({
'results': [dict(id=x, text=x) for x in results]
}))


urlpatterns = [
url(
'test-autocomplete/$',
YourListView.as_view(),
name='select2_list',
),
]
1 change: 1 addition & 0 deletions test_project/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def get_databases(base_dir):

# test apps
'select2_foreign_key',
'select2_list',
'select2_generic_foreign_key',
'select2_many_to_many',
'select2_one_to_one',
Expand Down
1 change: 1 addition & 0 deletions test_project/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
url(r'^linked_data/', include('linked_data.urls')),

url(r'^select2_foreign_key/', include('select2_foreign_key.urls')),
url(r'^select2_list/', include('select2_list.urls')),
url(r'^select2_generic_foreign_key/',
include('select2_generic_foreign_key.urls')),
url(r'^select2_generic_m2m/', include('select2_generic_m2m.urls')),
Expand Down