How to pass dynamic value for django-filter or django-autocomplete-light ?

Hello!
I want to add a dynamic initial value for django-filter or django-autocomplete-light to my DetailView. I don’t know how best to build it with django-filter, django-autocomplete-light or without third app.

I have a dependency dropdown (in my case, this is journal → journal year → journal volume) for each JournalDetailView. And I want to pass dynamic field for journal because I have ForeignKey which is used for depends dropdown for journal year and journal volume

For example, In this case I have three fields: journal, journal year and journal volume. I want to pass value depends on DetailView for journal. For instance, for journal “Nature” it will pass field “Nature”; for journal “Ca-A Cancer Journal for Clinicians” it will pass “Ca-A Cancer Journal for Clinicians”.

models.py


class Journal(models.Model):
    slug = models.SlugField(unique=True)
    name = models.CharField(max_length=100, blank=True, null=True)



class Article(models.Model, HitCountMixin):
    slug = models.SlugField(unique=True)
    journal = models.ForeignKey(
        "Journal", on_delete=models.SET_NULL, null=True, blank=True
    )
    journalyear = models.ForeignKey(
        "JournalYear", on_delete=models.SET_NULL, null=True, blank=True
    )
    journalvolume = models.ForeignKey(
        "JournalVolume", on_delete=models.SET_NULL, null=True, blank=True
    )

    def __str__(self):
        return self.title



class JournalYear(models.Model):
    journal = models.ForeignKey(
        "Journal", on_delete=models.SET_NULL, null=True, blank=True
    )
    name = models.CharField(max_length=10, blank=True, null=True)

    def __str__(self):
        return self.name


class JournalVolume(models.Model):
    journalyear = models.ForeignKey(
        "JournalYear", on_delete=models.CASCADE, blank=True, null=True
    )
    name = models.CharField(max_length=10, blank=True, null=True)

    def __str__(self):
        return self.name

views.py

class JournalAutocomplete2(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        # Don't forget to filter out results depending on the visitor !
        if not self.request.user.is_authenticated:
            return Journal.objects.none()

        qs = Journal.objects.all()

        if self.q:
            qs = qs.filter(name__icontains=self.q)
            
        return qs


class JournalYearAutocomplete2(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        # Don't forget to filter out results depending on the visitor !
        if not self.request.user.is_authenticated:
            return JournalYear.objects.none()

        journal = self.forwarded.get("journal")

        qs = JournalYear.objects.all()

        if journal:
            qs = qs.filter(journal=journal)

        if self.q:
            qs = qs.filter(name__icontains=self.q)

        return qs


class JournalVolumeAutocomplete2(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        # Don't forget to filter out results depending on the visitor !
        if not self.request.user.is_authenticated:
            return JournalVolume.objects.none()

        qs = JournalVolume.objects.all()

        journalyear = self.forwarded.get("journalyear", None)

        if journalyear:
            qs = qs.filter(journalyear=journalyear)

        if self.q:
            qs = qs.filter(name__icontains=self.q)

        return qs

class JournalListView(ListView):
    model = Journal
    template_name = "journals/journals.html"
    context_object_name = "journals"

class JournalDetailView(DetailView):
    model = Journal
    template_name = "journals/journal_detail.html"
    context_object_name = "journal"

    def get_context_data(self, **kwargs):
        context_data = super(JournalDetailView, self).get_context_data()
        journal_slug = self.kwargs.get("slug", None)
        f = JournalFilter(
            self.request.GET,
            queryset=Article.objects.filter(
                journal__slug__exact=self.kwargs["slug"]),
        )
        context_data["filter"] = f
        return context_data

urls.py


urlpatterns = [
    path("journals/", JournalListView.as_view(), name="journals"),
    path("journal/<str:slug>", JournalDetailView.as_view(), name="journal_detail"),
    # Django-autocomplete-light
    path(
        "journalyearautocomplete2/",
        JournalYearAutocomplete2.as_view(),
        name="journalyearautocomplete2",
    ),
    path(
        "journalvolumeautocomplete2/",
        JournalVolumeAutocomplete2.as_view(),
        name="journalvolumeautocomplete2",
    ),
    path(
        "journalautocomplete2/",
        JournalAutocomplete2.as_view(),
        name="journalautocomplete2",
    ),
]

filters.py


class JournalFilter(django_filters.FilterSet):
    journal = django_filters.ModelChoiceFilter(
        queryset=Journal.objects.all(),
        widget=autocomplete.ModelSelect2(url="journalautocomplete2"),
    )
    journalyear = django_filters.ModelChoiceFilter(
        queryset=JournalYear.objects.all(),
        widget=autocomplete.ModelSelect2(
            url="journalyearautocomplete2", forward=["journal"]
        ),
    )
    journalvolume = django_filters.ModelChoiceFilter(
        queryset=JournalVolume.objects.all(),
        widget=autocomplete.ModelSelect2(
            url="journalvolumeautocomplete2", forward=["journalyear"]
        ),
    )

    class Meta:
        model = Article
        fields = {"journalyear", "journalvolume", "journal"}

You’ll need to use JavaScript really for this. When the first select is updated, load in the values for the second, and so on.

The HTMX library is a great option here — it makes a very light add-on to Django. They even have a Cascading Selects example in their docs.

Carlton, hello!
Thank you for response!
Please could you give an example how to build it for dynamic initial first select(in this case it’s a journal)?

Carlton, I update my question with screenshoots. I think It will be useful for explanation.

I have this

I want it

But I want it to be done automatically without user selection

I hope that it became clearer if I explained it poorly first. I think it’s easier to explain by screenshoots

Edit:
It works approach for readers.

def article_list(request, slug):
    journal = get_object_or_404(Journal, slug=slug)
    my_filter_defaults = {'journal': journal}
    f = JournalFilter(request.GET or my_filter_defaults)
    return render(request, 'journals/journaldetail_list.html', {'filter': f, 'journal': journal})

To pass dynamic values for Django filters or Django autocomplete-light, you can 1. Pass dynamic values in the view: You can pass dynamic values to Django filters or autocomplete-light in the view by defining a function that generates the dynamic value based on some context or user input. For example, you can define a function that gets the current user’s ID and use it to filter results. Then, you can pass this function as a parameter to the filter or autocomplete-light queryset .
from django.shortcuts import render
from django_filters import FilterSet
from .models import MyModel

class MyFilter(FilterSet):
class Meta:
model = MyModel
fields = [‘field1’, ‘field2’]

def my_view(request):
current_user_id = request.user.id
queryset = MyModel.objects.filter(user_id=current_user_id)
my_filter = MyFilter(request.GET, queryset=queryset)
return render(request, ‘my_template.html’, {‘filter’: my_filter})

Side note: When posting code here, please be sure to surround the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then the code, then another line of ```. This ensures the code remains properly formatted, which is critical with Python.

if you want to pass a dynamic value to an autocomplete widget, you can use ’ get_form_kwargs’ method of view to modify the kwrd arguments passed to widget form.

To pass dynamic values for Django filters or Django-autocomplete-light, you can use a combination of URL parameters, form inputs, or JavaScript.

Here’s an example of how you can achieve this:

  1. Using URL parameters:
  • Define your URL pattern in urls.py with a parameter placeholder:
path('my_view/<str:dynamic_value>/', views.my_view, name='my_view'),

In your view function, access the dynamic value using the parameter name:

def my_view(request, dynamic_value):
    # Use dynamic_value in your filtering or autocomplete logic
    ...

When generating the URL, pass the dynamic value as an argument:

url = reverse('my_view', args=[dynamic_value])

  1. Using form inputs:
  • Create a form with an input field to capture the dynamic value:
class MyForm(forms.Form):
    dynamic_value = forms.CharField()

In your view function, instantiate the form and access the value:

def my_view(request):
    form = MyForm(request.GET or None)
    if form.is_valid():
        dynamic_value = form.cleaned_data['dynamic_value']
        # Use dynamic_value in your filtering or autocomplete logic
        ...

In your template, render the form and submit it:

<form method="GET" action="{% url 'my_view' %}">
    {{ form }}
    <button type="submit">Submit</button>
</form>

Using JavaScript:

  • Include the necessary JavaScript libraries for autocomplete (e.g., jQuery, Django-autocomplete-light).
  • Create an input field in your template and assign it an ID for JavaScript access:
<input type="text" id="dynamic-input" />

In your JavaScript code, initialize the autocomplete functionality:

$(document).ready(function() {
    $('#dynamic-input').autocomplete({
        // Configure the autocomplete options
        source: '/my_autocomplete_endpoint/',
        // Handle the selected value
        select: function(event, ui) {
            var dynamicValue = ui.item.value;
            // Use dynamicValue in your filtering or autocomplete logic
            ...
        }
    });
});

Create an endpoint in your Django views to handle autocomplete requests:

from dal import autocomplete

class MyAutocomplete(autocomplete.Select2ListView):
    def get_list(self):
        # Retrieve the dynamic values based on the search term
        ...

Map this endpoint in urls.py :slight_smile:

path('my_autocomplete_endpoint/', views.MyAutocomplete.as_view(), name='my_autocomplete_endpoint'),

These approaches allow you to pass dynamic values to Django filters or Django-autocomplete-light and use them in your filtering or autocompletion logic. Choose the method that best suits your specific requirements and use case.