Defining Lookups¶
What are Lookups?¶
Lookups define the corresponding ajax views used by the auto-completion fields and widgets. They take in the current request and return the JSON needed by the jQuery auto-complete plugin.
Defining a Lookup¶
django-selectable uses a registration pattern similar to the Django admin.
Lookups should be defined in a lookups.py in your application’s module. Once defined
you must register in with django-selectable. All lookups must extend from
selectable.base.LookupBase
which defines the API for every lookup.
from selectable.base import LookupBase from selectable.registry import registry class MyLookup(LookupBase): def get_query(self, request, term): data = ['Foo', 'Bar'] return [x for x in data if x.startswith(term)] registry.register(MyLookup)
Lookup API¶
-
LookupBase.
get_query
(request, term)¶ This is the main method which takes the current request from the user and returns the data which matches their search.
Parameters: - request – The current request object.
- term – The search term from the widget input.
Returns: An iterable set of data of items matching the search term.
-
LookupBase.
get_item_label
(item)¶ This is first of three formatting methods. The label is shown in the drop down menu of search results. This defaults to
item.__unicode__
.Parameters: item – An item from the search results. Returns: A string representation of the item to be shown in the search results. The label can include HTML. For changing the label format on the client side see Advanced Label Formats.
-
LookupBase.
get_item_id
(item)¶ This is second of three formatting methods. The id is the value that will eventually be returned by the field/widget. This defaults to
item.__unicode__
.Parameters: item – An item from the search results. Returns: A string representation of the item to be returned by the field/widget.
-
LookupBase.
split_term
(term)¶ - Split searching term into array of subterms that will be searched separately. You can override this function to achieve different splitting of the term.
Parameters: term – The search term. Returns: Array with subterms
-
LookupBase.
get_item_value
(item)¶ This is last of three formatting methods. The value is shown in the input once the item has been selected. This defaults to
item.__unicode__
.Parameters: item – An item from the search results. Returns: A string representation of the item to be shown in the input.
-
LookupBase.
get_item
(value)¶ get_item
is the reverse ofget_item_id
. This should take the value from the form initial values and return the current item. This defaults to simply return the value.Parameters: value – Value from the form inital value. Returns: The item corresponding to the initial value.
-
LookupBase.
create_item
(value)¶ If you plan to use a lookup with a field or widget which allows the user to input new values then you must define what it means to create a new item for your lookup. By default this raises a
NotImplemented
error.Parameters: value – The user given value. Returns: The new item created from the item.
-
LookupBase.
format_item
(item)¶ By default
format_item
creates a dictionary with the three keys used by the UI plugin: id, value, label. These are generated from the calls toget_item_id
,get_item_value
andget_item_label
. If you want to add additional keys you should add them here.The results of
get_item_label
is conditionally escaped to prevent Cross Site Scripting (XSS) similar to the templating language. If you know that the content is safe and you want to use these methods to include HTML should mark the content as safe withdjango.utils.safestring.mark_safe
inside theget_item_label
method.get_item_id
andget_item_value
are not escapted by default. These are not a XSS vector with the built-in JS. If you are doing additional formating using these values you should be conscience of this fake and be sure to escape these values.Parameters: item – An item from the search results. Returns: A dictionary of information for this item to be sent back to the client.
There are also some additional methods that you could want to use/override. These are for more advanced use cases such as using the lookups with JS libraries other than jQuery UI. Most users will not need to override these methods.
-
LookupBase.
format_results
(self, raw_data, options)¶ Returns a python structure that later gets serialized. This makes a call to paginate_results prior to calling format_item on each item in the current page.
Parameters: - raw_data – The set of all matched results.
- options – Dictionary of
cleaned_data
from the lookup form class.
Returns: A dictionary with two keys
meta
anddata
. The value ofdata
is an iterable extracted from page_data. The value ofmeta
is a dictionary. This is a copy of options with one additional elementmore
which is a translatable “Show more” string (useful for indicating more results on the javascript side).
-
LookupBase.
paginate_results
(results, options)¶ If SELECTABLE_MAX_LIMIT is defined or
limit
is passed in request.GET thenpaginate_results
will return the current page using Django’s built in pagination. See the Django docs on pagination for more info.Parameters: - results – The set of all matched results.
- options – Dictionary of
cleaned_data
from the lookup form class.
Returns: The current Page object of results.
Lookups Based on Models¶
Perhaps the most common use case is to define a lookup based on a given Django model.
For this you can extend selectable.base.ModelLookup
. To extend ModelLookup
you
should set two class attributes: model
and search_fields
.
from __future__ import unicode_literals from selectable.base import ModelLookup from selectable.registry import registry from .models import Fruit class FruitLookup(ModelLookup): model = Fruit search_fields = ('name__icontains', ) registry.register(FruitLookup)
The syntax for search_fields
is the same as the Django
field lookup syntax.
Each of these lookups are combined as OR so any one of them matching will return a
result. You may optionally define a third class attribute filters
which is a dictionary of
filters to be applied to the model queryset. The keys should be a string defining a field lookup
and the value should be the value for the field lookup. Filters on the other hand are
combined with AND.
User Lookup Example¶
Below is a larger model lookup example using multiple search fields, filters and display options for the auth.User model.
from django.contrib.auth.models import User from selectable.base import ModelLookup from selectable.registry import registry class UserLookup(ModelLookup): model = User search_fields = ( 'username__icontains', 'first_name__icontains', 'last_name__icontains', ) filters = {'is_active': True, } def get_item_value(self, item): # Display for currently selected item return item.username def get_item_label(self, item): # Display for choice listings return u"%s (%s)" % (item.username, item.get_full_name()) registry.register(UserLookup)
Lookup Decorators¶
Registering lookups with django-selectable creates a small API for searching the lookup data. While the amount of visible data is small there are times when you want to restrict the set of requests which can view the data. For this purpose there are lookup decorators. To use them you simply decorate your lookup class.
from django.contrib.auth.models import User from selectable.base import ModelLookup from selectable.decorators import login_required from selectable.registry import registry @login_required class UserLookup(ModelLookup): model = User search_fields = ('username__icontains', ) filters = {'is_active': True, } registry.register(UserLookup)
Note
The class decorator syntax was introduced in Python 2.6. If you are using django-selectable with Python 2.5 you can still make use of these decorators by applying the without the decorator syntax.
class UserLookup(ModelLookup):
model = User
search_fields = ('username__icontains', )
filters = {'is_active': True, }
UserLookup = login_required(UserLookup)
registry.register(UserLookup)
Below are the descriptions of the available lookup decorators.
ajax_required¶
The django-selectable javascript will always request the lookup data via XMLHttpRequest (AJAX) request. This decorator enforces that the lookup can only be accessed in this way. If the request is not an AJAX request then it will return a 400 Bad Request response.
login_required¶
This decorator requires the user to be authenticated via request.user.is_authenticated
.
If the user is not authenticated this will return a 401 Unauthorized response.
request.user
is set by the django.contrib.auth.middleware.AuthenticationMiddleware
which is required for this decorator to work. This middleware is enabled by default.
staff_member_required¶
This decorator builds from login_required
and in addition requires that
request.user.is_staff
is True
. If the user is not authenticatated this will
continue to return at 401 response. If the user is authenticated but not a staff member
then this will return a 403 Forbidden response.