Введение
Периодически на форуме возникает вопрос: как сделать зависимый выбор, например, категорий и товаров выбранной категории? Задача несложная, довольно быстро делается на JS, но так как приходится постоянно тратить на это время, я решил закрыть этот вопрос окончательно.
Первым делом был найдено приложение django-smart-select. Оно решало проблему, но как-то сложно. Я так и не понял, зачем для решения проблемы отображения опускаться до уровня моделей? В итоге, этот вариант был отброшен, но послужил хорошим фундаментом для собственного приложения.
Рассмотрим пример: У вас на сайте посетитель выбирает категорию, а затем в следующем же поле видит только товары, выбранной категории. Эта задача решается с помощью виджета поля.
Установка
Совет: Используйте виртуальное окружение!
Для установки приложения надо выполнить:
pip install -e git+git://github.com/RaD/django-chained-selects#egg=chained-selects
Добавить следующую строку в раздел INSTALLED_APPS
в файле settings.py
:
'chained_selects',
Добавить следующую строку в главный urls.py
:
url(r'^chained/', include('chained_selects.urls')),
Использование
Допустим, у нас есть следующие модели:
class Category(models.Model):
title = models.CharField(max_length=64)
class Item(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=64)
is_present = models.BooleanField(default=False)
Вы должны написать метод модели, который будет возвращать выборку. Выполняйте необходимую фильтрации выборки в этом методе. Вы можете назвать этот метод как угодно. Например:
class Category(models.Model):
...
def chained_relation(self):
return self.item_set.filter(is_present=True)
Создайте форму:
from chained_selects.widgets import ChainedSelectWidget
class TestForm(forms.Form):
category = forms.ModelChoiceField(queryset=Category.objects.all())
item = forms.ModelChoiceField(queryset=Item.objects.all())
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
if 0 == len(self.data):
# чистим выборку при простом отображении формы
self.fields['item'].queryset = Item.objects.none()
# назначаем виджет на второй селект
self.fields['item'].widget = ChainedSelectWidget(
parent_name='category', # имя первого селекта
app_name='app_name', # имя приложения, где лежит модель с методом
model_name='category', # имя модели с методом
method_name='chained_relation', # имя самого метода
)
Далее используйте метод как обычно.
Следует отметить, что на странице с формой должна загружаться jQuery.
Репозиторий приложения django-chained-selects.
P.S. Благодарности лучше видеть в истории Яндекс.Бабло :)