I
I
Igor Karpov2014-01-14 16:23:03
Django
Igor Karpov, 2014-01-14 16:23:03

Django: queryset filters based on current url (currently selected site category)?

The essence of the problem
There is a data model, like `Organization` <- (fk) `Project` <- (fk) `Resource`. When a user selects an organization in the header, they are taken to the detail view, for example "/organization/1/details/". Now it is necessary that all links to projects and resources (for example, in the sidebar) display only information related to the selected organization, i.e. a filter would be added to the queryset, such as
qs &= Q(project__organization=organization_id)
UPD1

That is, the choice of a project or organization is actually a change in the current display context. In the context of the selected organization, all links lead only to projects for that organization, and so on.

How I tried to do it
1. Optional arguments in 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'),

This method requires, first, rewriting all links in the templates.
`{% url 'projects' org.id %}` 
`{% url 'resources' org.id  prj.id %}`

Secondly, there is a problem with constantly passing organization_id (filter_field) to templates. This problem can be relatively easily solved using middleware, for example, like this:
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)

Along the way, you can write a context_processor that will put everything you need into the context (in case the request does not fall into the templates).
Of course, the rest of the unrewritten links will work the same as before, but will break the navigation, because the filtering parameters (organization_id, project_id, etc) must always be present in the url, otherwise they will be "lost". For example, editing a user profile should not contain these filters. Therefore, this method has the following disadvantages:
  • modifying all {% url %}, or deliberately hiding links that will break navigation, making life difficult for users
  • heavy complication of urls.py
  • additional overhead for writing code that checks for filters

2. COOKIE, or GET
GET - a method that will spoil the "beauty" of the url; I have not tried COOKIE yet, but I avoid using them in every possible way.
3. Storing the current filter in the session, or in the cache.
The method is easy to use, but it has the disadvantage that opening the site in another tab will mess up the navigation on the first tab.
4. Full-ajax navigation + rewriting the necessary links, for example with Django.js.url .
overkill.
I've run out of ideas.

Answer the question

In order to leave comments, you need to log in

4 answer(s)
Y
Yuri Shikanov, 2014-01-14
@dizballanze

The first option is the most transparent.

S
SilentSokolov, 2014-01-14
@SilentSokolov

Honestly, I did not understand the question, but I will insert five cents.
First you need to read the documentation .
Now in the project/organization we add the method:

@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})

Now, anywhere in the project where, for example, projects are available:
{% for p in projects %}
    {{ p.get_absolute_url }} # выведет /organization/1/details/2/
{% endfor %}

PS {% url 'resources' org.id prj.id %} is a terrible design

A
ASPAnt, 2014-01-14
@ASPAnt

The session is the best option in my opinion. And at the expense of opening in another tab, it will spoil the navigation in the current one, so this is only after reloading the current tab.

L
leclecovich, 2014-01-14
@leclecovich

Try like this:

organization = Organization.objects.get(id=organization_id)
projects = organization.organization_set.filter(...)

Here is the materiel.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question