===============================
Another Ajax Auto-Complete, Yet
===============================
.. image:: https://coveralls.io/repos/Starou/django-yaaac/badge.png
:target: https://coveralls.io/r/Starou/django-yaaac
.. image:: https://img.shields.io/pypi/v/django_yaaac.svg
:target: https://pypi.python.org/pypi/django-yaaac
.. image:: https://img.shields.io/pypi/pyversions/django_yaaac.svg
:target: https://pypi.python.org/pypi/django-yaaac/
:alt: Supported Python versions
.. image:: https://img.shields.io/pypi/l/django_yaaac.svg
:target: https://pypi.python.org/pypi/django-yaaac/
:alt: License
.. image:: https://travis-ci.org/Starou/django-yaaac.svg
:target: https://travis-ci.org/Starou/django-yaaac
:alt: Travis C.I.
*Yaaac* is lightweight Django application providing Ajax search to foreign-key form fields.
Version 3 upgrade warning
=========================
This version brings Python 3.6 compatibility with a minor regression.
The default ``suggest_by`` is now ``__str__`` instead of ``__unicode__`` so check
the `Django documentation <https://docs.djangoproject.com/en/1.11/topics/python3/#str-and-unicode-methods>`_
to migrate your code.
Django 2-2 and Version 3.2 important note
=========================================
Since Django-2.2 the way `assets are sorted has been completely rewritten <https://docs.djangoproject.com/en/2.2/releases/2.2/#merging-of-form-media-assets>`_
and as the result breaks this lib in the admin. The fix was to embbed jQuery and set the
dependency in the widgets Media classes which may leads to other regressions.
Since a `autocomplete solution <https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields>`_
is now built-in Django admin, the support for the admin has been removed.
Installation
============
Install the app from the source::
python setup.py build && (sudo) python setup.py install
Or with *pip*::
pip install django-yaaac
(for Django < 1.8, use a previous version like ``pip install django-yaaac==1.9.0``)
Add the app in your settings.INSTALLED_APPS:
.. code-block:: python
INSTALLED_APPS = [
...,
"django_yaaac",
...,
]
In the *urls.py* module of your project, define the url pattern for ajax calls:
.. code-block:: python
from django_yaaac.manager import autocomplete
from django.conf.urls import url
urlpatterns = [
...
url(r'^yaaac/', autocomplete.urls),
...
]
Usage
=====
Forms
-----
What you need to do is to declare a custom *ModelForm*:
.. code-block:: python
from django import forms
from django.contrib import admin
from django_yaaac.forms.fields import AutocompleteModelChoiceField
from . import models
class BandMemberForm(forms.ModelForm):
band = AutocompleteModelChoiceField(site=admin.site,
queryset=models.Band.objects.all(),
yaaac_opts={
"search_fields": ["^name"],
"suggest_by": "get_full_name",
"min_chars": 3, # Fire search when 3 chars are sent (1 by default.)
"max_height": 400, # 300px by default.
"width": 250, # 300px by default.
},
required=True)
class Meta:
model = models.BandMember
class Media:
# You need jQuery. Don't forget to call {{ form.media }} in your template.
js = ('js/jquery.min.js', )
admin.site.register(models.BandMember, BandMemberAdmin)
The *site* parameter of *AutocompleteModelChoiceField* is required for related lookup (the
magnifier glass). The *search_fields* is a list of fields to search against using the same syntax
as in Django Admin (^, $ etc).
Extra options *min_chars*, *max_height* and *width* are the counter-part of *minChars*, *maxHeight* and *width*
in `Autocomplete options <https://github.com/devbridge/jQuery-Autocomplete#api>`_.
*suggest_by* is optional. It can be a field or a method of the model.
By default, suggestions are shown using *__unicode__* method.
If your model define a ``get_absolute_url()`` method, the label is a link to that resource.
Models
------
The ``Yaaac`` class must defines the following:
- ``user_passes_test`` is a class method that takes a user and return True or False.
- ``allows_suggest_by`` is a list of model fields or methods that can used as return value by the search view.
.. code-block:: python
class BandMember(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
band = models.ForeignKey("Band", null=True, blank=True)
favorite_instrument = models.ForeignKey("Instrument", null=True, blank=True)
class Meta:
unique_together = (('first_name', 'last_name'),)
class Yaaac:
user_passes_test = lambda instance, user: user and user.is_authenticated() or False
allows_suggest_by = ['get_full_name']
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
def get_full_name(self):
return u"%s %s" % (self.first_name, self.last_name)
Tuning
======
To ease the DOM manipulation, HTML classes are added to the elements. The most interesting being ``yaaac_<fieldname>``
to the hidden input storing the foreign key value. This is very convenient when you need to add behavior to a whole
set of fields - also those that don't exist when the page is created - sharing the same name.
Use jQuery delegation (i.e. ``$(".foo").on("change", ".yaaac_first_name")``) to place an event on one field for all
the inline forms present in the page or to come (i.e. Click on "Add a new Band Member".)