0

I have two querysets: type and age_group.

type queryset: <QuerySet [<Type: Cat>, <Type: Dog>, <Type: Other>]>

age_group queryset: <QuerySet [<AgeGroup: Young>, <AgeGroup: Baby>, <AgeGroup: Adult>, <AgeGroup: Senior>]>

I loop through these from within my template form so that I can grab the pk when one has been selected, but I cannot capture the variable from within the for loop. How do I capture a variable from within a for loop when using Django? I want to capture pk for type and pk for age_group and then use both to filter the model Animal and return a filtered list that matches the user's preferences. A directory search function, essentially.

Template:

    {% extends 'base.html' %}

    {% block content %}
        <h1>Animal Search</h1>

    <form class="form-inline" action= '.' method="post">
            {% csrf_token %}
       
         <select name= "TypeSearch" class="custom-select my-1 mr-sm-2" id="animal_list_type">
         <label class="sr-only type" for="animal_list_type">SEARCH</label>
             {% for i in animal_type_list %}
                    <option value="{{i.pk}}">{{i}}</option> #how to capture the selected pk??
            {% endfor %}
         </select>

         <select name="AgeSearch" class="custom-select my-1 mr-sm-2" id="animal_list_ageGroup">
         <label class="sr-only ageLabel" for="animal_list_ageGroup">SEARCH</label>
             {% for j in age_group_list %}
                    <option value="{{j.pk}}">{{j}}</option> #how to capture the selected pk??
            {% endfor %}
         </select>

        <input type="submit" value="SEARCH" onclick="window.location='{% url 'animals:matches_list' pk=4 %}'; return false;">
        <input type="submit" onclick="window.location='{% url 'animals:animals' %}'; return false;" value="Cancel">
    </form>

    {% endblock %}
  

views.py

    class VisitorSearchView(View):
    def get(self, request, pk=None):
        #first tried ModelForm but couldn't figure out how to capture and iterate through one field of value options at a time
        animalList = Animal.type.get_queryset()
        animalList2 = Animal.ageGroup.get_queryset()
        context = {
            "animal_type_list": animalList,
            "age_group_list": animalList2
            }

        return render(request, "animals/landing.html", context)

    def post(self, request, pk=None):
        theForm1 = AnimalSearchForm(request.POST)
        success_url = reverse_lazy('animals:matches_list')
        print(pk)
        print(theForm1)
        filler_for_now = Animals.objects.all()
        context = {
                'theMatches': filler_for_now
            }
        return render(request, success_url, context)

model.py

    class Animal(models.Model):
        name = models.CharField(max_length=500, blank=False, null=False)
        type = models.ForeignKey(Type, on_delete=models.SET_NULL, blank=False, null=True)
        ageGroup = models.ForeignKey(AgeGroup, max_length=300, on_delete=models.SET_NULL, blank=False, null=True)
        age = models.PositiveIntegerField(blank=False, null=False)
        sex = models.CharField(max_length=100, choices=SEX, blank=False, null=False, default='NA')
        breedGroup = models.ManyToManyField(BreedGroup, blank=False)
        breed = models.ManyToManyField(Breed, blank=False)
        tagLine = models.CharField(max_length=300, blank=False, null=False)
        goodWithCats = models.BooleanField(blank=False, null=False, default='Not Enough Information')
        goodWithDogs = models.BooleanField(null=False, blank=False, default='Not Enough Information')
        goodWKids = models.BooleanField(null=False, blank=False, default='Not Enough Information')

urls.py

    app_name = 'animals'

urlpatterns = [

    path('', views.AnimalListView.as_view(), name='animals'),
    path('landing/', views.VisitorSearchView.as_view(), name='landing'),
    path('matches/<int:pk>', views.VisitorSearchView.as_view(), name='matches_list'),

]

forms.py #(originally tried to use ModelForm but couldn't figure out how to grab the pk for both chooseType and chooseAge fields so chose to try to just use querysets from view)

    class AnimalSearchForm(ModelForm):
    chooseType = ModelChoiceField(queryset=Animal.objects.values_list('type', flat=True).distinct(),empty_label=None)
    chooseAge  = ModelChoiceField(queryset=Animal.objects.values_list('ageGroup', flat=True).distinct(), empty_label=None)
    class Meta:
        model = Animal
        exclude = '__all__'

Outside of Django, this would be a simple problem to solve. How do I capture a variable from within a for loop when using Django? I have tried to instantiate a variable outside the for-loop and then update that based off selection from within, but it seems that this cannot be done via the template...?

Zuckerbrenner
  • 323
  • 2
  • 15

2 Answers2

1

I think you need to remove the dot from the action attribute. Empty string in action use the current URL for form submission. Form opening line will be like

 <form class="form-inline" action= '' method="post">
Sohaib
  • 566
  • 6
  • 15
1

Well the real issue here is that you really should be using FormView to display a form together with DetailView to display model data, in this particular case you should do something like this:

views.py

from django.views.generic import FormView, DetailView

class VisitorSearchView(FormView, DetailView):
    model = Animal
    template_name = 'animals/landing.html'
    form_class = AnimalSearchForm

    def form_valid(self, form):
        data = form.cleaned_data # Dict of submitted data
        # handle form validation and redirect

    def get_context_data(self, request):
        context = super(VisitorSearchView, self).get_context_data(**kwargs)
        animals = Animal.objects.all() # or use a custom filter
        context['animals'] = animals
        return context

Then in your landing.html

where you want a list of animal types:

{% for animal in animals %}
    {{ animal.type }}
{% endfor %}

and where you want a list of animal ages:

{% for animal in animals %}
    {{ animal.age }}
{% endfor %}

declare your form normally as you would.

Arjun Ariyil
  • 386
  • 3
  • 14
SLDem
  • 2,065
  • 1
  • 8
  • 28
  • And just use `{{ form.as_p }}` to show your form, don`t do any of this unnecessary shenenigans. – SLDem Dec 28 '20 at 21:49
  • Thanks. I tried this and I get the same problem of not being able to capture the pk value because it is in a for loop. Perhaps I'm misunderstanding. Are you saying I don't need to pass the pk value if I use FormView? What is the benefit of using FormView? The docs didn't explain it much. – Zuckerbrenner Dec 29 '20 at 02:46
  • 1
    With Form View, you can access the submitted data by using `form.cleaned_data` inside `form_valid` method. Override `form_valid` method and do redirection there as mentioned in the answer. – Arjun Ariyil Dec 29 '20 at 04:29
  • 1
    But you are still writing a lot of unnecessary code. Please refer to the example form view in https://docs.djangoproject.com/en/3.1/ref/class-based-views/generic-editing/#formview. You can simplify your work a lot by following the Django way of coding. – Arjun Ariyil Dec 29 '20 at 04:39
  • 1
    you can pass the pk in the same way you do with regular views, to get it inside the CBV use `self.kwargs['pk']` – SLDem Dec 29 '20 at 05:24
  • 1
    the benefits of generic CBVs is that you write significantly less code – SLDem Dec 29 '20 at 05:25
  • @ArjunAriyil @SLDem This has helped a lot. I am able to return one match using `DetailView` as expected but I cannot return all matches if I modify for `ListView`. I am not sure why. `animals = Animal.objects.get_queryset().filter(Q(type=self.kwargs['pk']) & Q(ageGroup=self.kwargs['pk2']))` is returning an empty queryset. If I set context to `context['animals'] = animals` then I get the first match. If I set context to `context = {'animals':animals}` I get no matches. I want a list of all matches. I am sure this is super simple and hopefully one day it will be for me. – Zuckerbrenner Dec 30 '20 at 05:13
  • What exactly would you like to see in your `animals` queryset? – SLDem Dec 30 '20 at 11:40
  • @SLDem So if user selects "dog" `type` and "baby" `ageGroup` then I would want queryset to have all animal entries that match those two fields...so a queryset of puppies and then would, through `ListView`, show the queryset as a list of matches. Here I have the queryset of puppies currently in db: `, , ]`. I have only been able to return the first match (Lambert). – Zuckerbrenner Dec 30 '20 at 20:11
  • take a look at this question: I think you might find what you need https://stackoverflow.com/questions/291945/how-do-i-filter-foreignkey-choices-in-a-django-modelform – SLDem Dec 30 '20 at 20:37