6

I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.

Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.

I have models.py as follows:

class Product(models.Model):
    category = models.ForeignKey("Category")
    sub_category = models.ForeignKey("SubCategory")

class Category(models.Model):
    name = models.CharField(max_length=256)

class SubCategory(models.Model):
    category = models.ForeignKey("Category")
    name = models.CharField(max_length=256)

And my forms.py includes:

class BasicSearchForm(forms.Form):
    category = forms.ModelChoiceField(
        label='Category',
        queryset=Category.objects.all(),
        to_field_name="name",
        empty_label=None,
        initial="Red") 
    sub_category = forms.ModelMultipleChoiceField(
        required=False,
        label='Type',
        queryset= SubCategory.objects.all(),
        to_field_name="name",
        widget=forms.Select)

And my views.py includes:

def search(request):
    if request.method == 'POST':
        form = BasicSearchForm(request.POST)
        if form.is_valid():
            category = form.cleaned_data['category']
            sub_category = form.cleaned_data['sub_category']
            return render(request, 'myapp/search.html', {'form': form})
    else:
        form = BasicSearchForm()
        return render(request, 'myapp/search.html', {'form': form})

And finally the search.html includes:

<form class="search-form" role="search" action="/search/" method="get"> 
    {{ form }} 
    <input type="submit" value="Search" />
</form>

I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!

Update: Thanks for the feedback. As a result I updated the following:

In my urls.py:

urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),

And in my views.py:

def update_subcategories(request):
    category = request.GET.get('category', None)
    sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
    return JsonResponse(sub_category, safe=False)

And I have this in my myapp/search.html:

{% block javascript %}
<script>
    $("#id_category").change(function () {
        var category = $(this).val();
        $.ajax({
            url: '{% url "myapp:update_subcategories" %}',
            data: {
                'category': category,
            },
            success: function (response) {
                var  new_options = response; 
                alert(new_options[0].name);  // works
                $('#id_sub_category').empty();
                $.each(new_options, function(key, value) {   
                    $('#id_sub_category')
                        .append($('<option>', { value : key })
                        .text(value.name)); 
                });
            }
    });
</script>
{% endblock %}

Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.

Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.

Luce Rawds
  • 91
  • 1
  • 4
  • 1
    You cannot do this from the server side. Create an API for this using django-rest-framework or similar and then use javascriping to implement the form on the client side – dkarchmer May 12 '17 at 13:45

2 Answers2

3

You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.

For a simple example, you could refer here

How do I POST with jQuery/Ajax in Django?

EDIT

def update_subcategories(request):
    category = request.GET.get('category', None)
    sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
    return JsonResponse(dict(sub_category=sub_category))

Then in ajax response you could grab it like response.data.sub_category

Community
  • 1
  • 1
zaidfazil
  • 9,017
  • 2
  • 24
  • 47
  • Thanks for the help. I figured this must be the case. Does anyone know if there is an example out there somewhere? – Luce Rawds May 14 '17 at 07:10
  • I'm struggling a bit with the update to the options. Don't suppose you're aware of a post that deals with this? Im getting data in that response of the form [{"name": "value1"}, {"name": "value2"}, etc – Luce Rawds May 15 '17 at 15:05
  • That, you have to do in JavaScript to convert it to a suitable format. And then from the server side also catch the data, however you want. – zaidfazil May 16 '17 at 02:50
  • The problem I'm having now is that I don't seem to be appending the correct information. I keep getting [object Object] repeated as my options although I can see from the alert that the correct data is there. I've tried solutions from a few answers that I've found but keep having the same issue. – Luce Rawds May 17 '17 at 09:12
  • can you post the views? – zaidfazil May 17 '17 at 09:14
  • thats becz, new_options variable a django object, you may need to stringify it or send from the view as json data – zaidfazil May 17 '17 at 09:25
  • Really appreciate your comments. Looks like I got it to work. Is there anything wrong with the approach I went for? – Luce Rawds May 17 '17 at 10:07
  • Just to let you know that this fails when using the back button as per my edit above. Any idea how to fix this? – Luce Rawds Jun 22 '17 at 13:00
0

Use ajax to send the category and retrieve subcategory elements.

For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.

T K Sourabh
  • 351
  • 2
  • 8