@lilo_panic

Django: queryset фильтры на основе текущего url (текущей выбранной категории сайта)?

Суть проблемы

Существует модель данных, вида `Organization` <- (fk) `Project` <- (fk) `Resource`. Когда юзер выбирает организацию в шапке, то попадает на detail view, например "/organization/1/details/". Теперь нужно, чтобы все ссылки на проекты и ресурсы (например, в сайдбаре) отображали только связанную с выбранной организацией информацию, т.е. к queryset добавлялся бы фильтр, типа
qs &= Q(project__organization=organization_id)

UPD1
То есть, выбор проекта, или организации - это на самом деле смена текущего контекста отображения. В контексте выбранной организации все ссылки ведут только на проекты для этой организации, и т.д.

Как я пробовал это делать

1. Опциональные аргументы в urls
url(r'^(?:organization/(?P<organization_id>\d+)/)?projects/$', 'app.views.projects', name='projects'),
url(r'^(?:organization/(?P<organization_id>\d+)/)?(?:project/(?P<project_id>\d+)/)?resources/$', 'app.views.resources', name='resources'),

Этот метод требует, во-первых, переписывания всех ссылок в шаблонах.
`{% url 'projects' org.id %}` 
`{% url 'resources' org.id  prj.id %}`

Во-вторых, есть проблема с постоянной передачей organization_id (filter_field) в шаблоны. Эту проблему сравнительно просто можно решить при помощи middleware, например, так:
class FetchFiltersMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):
        request.project_id = view_kwargs.get('project_id', 0)  # или .pop(), тогда не надо переписывать аргументы вьюх
        request.organization_id = view_kwargs.get('organization_id', 0)

Попутно можно написать context_processor, который поместит все нужное в контекст (на случай, если request не попадает в шаблоны).

Конечно, остальные не переписанные ссылки будут работать так же, как и раньше, но будут ломать навигацию, потому что параметры фильтрации (organization_id, project_id, etc) постоянно должны присутствовать в url, иначе они "потеряются". Например, редактирование профиля пользователя не должно содержать этих фильтров. Поэтому у этого метода следующие минусы:

  • модификация всех {% url %}, или намеренное сокрытие ссылок, которые сломают навигацию, что усложнит жизнь пользователям
  • сильное усложнение urls.py
  • дополнительные накладные расходы на написание кода, проверяющего наличие фильтров


2. COOKIE, или GET
GET - метод, который испортит "красоту" url; COOKIE я пока не пробовал, но всячески избегаю их использования.

3. Хранение текущего фильтра в сессии, или в кэше.
Метод легко использовать, но он плох тем, что открытие сайта на другой вкладке испортит навигацию на первой вкладе.

4. Full-ajax навигация + переписывание нужных ссылок, например при помощи Django.js.url.
Overkill.

У меня кончились идеи.
  • Вопрос задан
  • 4209 просмотров
Пригласить эксперта
Ответы на вопрос 4
dizballanze
@dizballanze
Software developer at Yandex
Первый вариант, как наиболее прозрачный.
Ответ написан
SilentSokolov
@SilentSokolov
Честно, вопроса я не понял, но вставлю пять копеек.

Для начала нужно прочитать документацию.

Теперь в проекте/организации добавляем метод:
@models.permalink
def get_absolute_url(self):
    return ('projects', (), {'org_id': self.pk}) # именованные аргументы у вас позиционные

@models.permalink
def get_absolute_url(self):
    return ('resources', (), {'org_id': self.org.pk, 'prj_id': self.pk})


Теперь в любом месте проекта, где доступены к примеру проекты:
{% for p in projects %}
    {{ p.get_absolute_url }} # выведет /organization/1/details/2/
{% endfor %}


P.S. {% url 'resources' org.id prj.id %} - ужасная конструкция
Ответ написан
ASPAnt
@ASPAnt
В сессии оптимальный вариант на мой взгляд. А на счет открытия в другой вкладке испортит навигацию в текущей, так это только после перезагрузки текущей вкладки.
Ответ написан
Комментировать
@leclecovich
Попробуйте так:
organization = Organization.objects.get(id=organization_id)
projects = organization.organization_set.filter(...)

Вот здесь матчасть.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы