Django autocomplete widgets and views using TomSelect
Project description
TomSelect for Django (MIZDB)
Django autocomplete widgets and views using TomSelect.
Note that this library was written specifically with the MIZDB app in mind - it may not apply to your app.
Installation
Install:
pip install -U mizdb_tomselect
Usage
Add to installed apps:
INSTALLED_APPS = [
...
"mizdb_tomselect"
]
Configure an endpoint for autocomplete requests:
# urls.py
from django.urls import path
from mizdb_tomselect.views import AutocompleteView
urlpatterns = [
...
path('autocomplete/', AutocompleteView.as_view(), name='my_autocomplete_view')
]
Use the widgets in a form.
from django import forms
from mizdb_tomselect.widgets import MIZSelect, MIZSelectTabular
from .models import MyModel
class MyForm(forms.Form):
mizselect = forms.ModelChoiceField(
MyModel.objects.all(),
widget=MIZSelect(
MyModel,
url='my_autocomplete_view',
search_lookup="name__icontains",
),
)
# Display results in a table, optionally with additional columns:
mizselect_tabular = forms.ModelChoiceField(
MyModel.objects.all(),
widget=MIZSelectTabular(
MyModel,
url='my_autocomplete_view',
search_lookup="name__icontains",
# extra_columns is a mapping of model field: column header label for extra columns
# (columns for valueField and labelField are always included)
extra_columns={'name': 'Name', 'something_else': 'Something Else'},
# The column header label for the labelField column
label_field_label='My Model Objects',
),
)
NOTE: Make sure to include bootstrap somewhere. For example in the template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MIZDB TomSelect Demo</title>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
{{ form.media }}
</head>
<body>
<div class="container">
<form>
{% csrf_token %}
{{ form.as_div }}
<button type="submit" class="btn btn-success">Save</button>
</form>
</div>
</body>
</html>
Widgets
The widgets pass attributes necessary to make autocomplete requests to the HTML element via the dataset property. The TomSelect element is then initialized from the attributes in the dataset property.
MIZSelect
Base autocomplete widget. The arguments of MIZSelect are:
Argument | Default value | Description |
---|---|---|
model | required | the model class that provides the choices |
url | "autocomplete" |
URL pattern name of the autocomplete view |
search_lookup | "name__icontains" |
the lookup to use when filtering the results |
value_field | "id" |
model field that provides the value of an option |
label_field | "name" |
model field that provides the label of an option |
create_field | model field to create new objects with (see below) | |
multiple | False | if True, allow selecting multiple options |
changelist_url | URL name of the changelist view for this model (see below) | |
add_url | URL name for the add view of this model(see below) | |
filter_by | a 2-tuple defining an additional filter (see below) |
MIZSelectTabular
This widget displays the results in tabular form. A table header will be added to the dropdown. By default, the table contains two columns: one column for the choice value (commonly the "ID" of the option) and one column for the choice label (the human-readable part of the choice).
To add more columns, pass an extra_columns
parameter to the widget. This must
be a mapping model field name: column label
. The field name tells TomSelect
what values to look up on a model object result for a given column. The label is
the table header label for a given column
MIZSelectTabular has the following additional arguments:
Argument | Default value | Description |
---|---|---|
extra_columns | a mapping for additional columns | |
value_field_label | "ID" |
table header for the value column |
label_field_label | "Object" |
table header for the label column |
Function & Features
Searching
The AutocompleteView filters the result queryset against the search_lookup
passed to the widget. The default value for the lookup is name__icontains
.
Overwrite the AutocompleteView.search
method to modify the search process.
class MyAutocompleteView(AutocompleteView):
def search(self, request, queryset, q):
# Filter using your own queryset method:
return queryset.search(q)
Option creation
To enable option creation in the dropdown, pass the URL pattern name for the add page of the given model to the widget. This will add an 'Add' button to the bottom of the dropdown.
# urls.py
urlpatterns = [
...
path('autocomplete/', AutocompleteView.as_view(), name='my_autocomplete_view'),
path('my_model/add', MyModelAddView.as_view(), name='my_model_add'),
]
# forms.py
widget = MIZSelect(MyModel, url='my_autocomplete_view', add_url='my_model_add')
Clicking on that button sends the user to the add page of the model.
AJAX request
If create_field
was also passed to the widget, clicking on the button will
create a new object using an AJAX POST request to the autocomplete URL. The
autocomplete view will use the search term that the user put in on the
create_field
to create the object:
class AutocompleteView:
def create_object(self, data):
"""Create a new object with the given data."""
return self.model.objects.create(**{self.create_field: data[self.create_field]})
Override the view's create_object
method to change the creation process.
Changelist link
The dropdown will include a link to the changelist of the given model if you pass in the URL pattern name for the changelist.
# urls.py
urlpatterns = [
...
path('autocomplete/', AutocompleteView.as_view(), name='my_autocomplete_view'),
path('my_model/change', MyModelChangelistView.as_view(), name='my_model_changelist'),
]
# forms.py
widget = MIZSelect(MyModel, url='my_autocomplete_view', changelist_url='my_model_changelist')
Filter against values of another field
Use the filter_by
argument to restrict the available options to the value of
another field. The parameter must be a 2-tuple: (name_of_the_other_form_field, django_field_lookup)
# models.py
class Pizza(models.Model):
best_topping = models.ForeignKey("Topping", on_delete=models.SET_NULL)
class Topping(models.Model):
...
# forms.py
class MyForm(forms.Form):
topping = forms.ModelChoiceField(queryset=Toppings.objects.all())
pizza = forms.ModelChoiceField(
queryset=Pizza.objects.all(),
wiget=MIZSelect(
Pizza,
filter_by=("topping", "best_topping_id")
)
)
This will result in the Pizza result queryset to be filtered against
best_topping_id
with the current value of the topping
formfield.
NOTE: When using filter_by
, the element now requires that the other field
provides a value. If the other field does not have a value, the search will not
return any results.
Development & Demo
python3 -m venv venv
source venv/bin/activate
make init
Then see the demo for a preview: python demo/manage.py runserver
Run tests with make test
or make tox
. To install required browsers for playwright: playwright install
.
See the makefile for other commands.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for mizdb_tomselect-0.2.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f47d56c5cd969cce0254a7d412633e274a88847dc9715a20e80f7bb72fb3e0ef |
|
MD5 | 5624b8bbf5cc3d92d4b61de5c9ff5a9c |
|
BLAKE2b-256 | d81d7c4cfed2d9b8dd0d9be3f33b7d6e5a78235bde9b83bf4a91eb0490766342 |