0

I have a form with multiple select inputs, as below:

<form method="POST" class="row" action="{% url 'solution_product_list'  %}">
{% csrf_token %}
    {#  main select div: usage or model? #}
    <div class="col-md-3 mx-md-5">
        <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب بر اساس:</h2>
        <select required aria-label="Select usage or model" id="usage_model_select" class="form-select" onchange="set_usage_or_model_dic()">
            <option selected>----</option>
            <option value="usage">کاربرد</option>
            <option value="model">مدل</option>
        </select>
    </div>
    {#  usage or model select div #}
    <div class="col-md-3 mx-md-5">
    {#  usage select div #}
        <div class="usage visually-hidden" id="usage_div">
            <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب کاربرد:</h2>
            <select required aria-label="Select usage" class="form-select"
                    name="usage_select" onchange="set_sub_usage_list()" id="usage_select_id">
                <option selected>----</option>
                {% for usage in usage_queryset %}
                    <option value="{{ usage.id }}">{{ usage.usage_name_fa }}</option>
                {% endfor %}
            </select>
        </div>
    {#  model select div #}
        <div class="model visually-hidden" id="model_div">
            <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب مدل:</h2>
            <select required aria-label="Select model" class="form-select"
                    name="model_select" onchange="set_pump_type_list()" id="model_select_id">
                <option selected>----</option>
                {% for model in main_model_queryset %}
                    <option value="{{ model.id }}">{{ model.model_name_fa }}</option>
                {% endfor %}
            </select>
        </div>
    </div>
    {# select sub_usage or pump_type div #}
    <div class="col-md-3 mx-md-5">
    {#  sub_usage select div #}
        <div class="sub_usage visually-hidden" id="sub_usage_div">
            <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب کاربرد جزئی:</h2>
            <select required aria-label="Select sub_usage" class="form-select" name="sub_usage_select">
                <option selected>همه‌ی کابردهای جزئی</option>
                {% for sub_usage in sub_usage_queryset %}
                    <option value="{{ sub_usage.id }}">{{ sub_usage.sub_usage_name_fa }}</option>
                {% endfor %}
            </select>
        </div>
    {#  model select div #}
        <div class="pump-type visually-hidden" id="pump_type_div">
            <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب تیپ:</h2>
            <select aria-label="Select pump_type" class="form-select" name="pump_type_select">
                <option selected>همه‌ی تیپ‌های این مدل</option>
                {% for pump_type in pump_type_queryset %}
                    <option value="{{ pump_type.id }}">{{ pump_type.type_name }}</option>
                {% endfor %}
            </select>
        </div>
    </div>
    <div>
        <input type="submit" value="مرحله بعدی" id="submit" class="btn btn-primary">
    </div>
</form>

As you can see, the action of my form is "{% url 'solution_product_list' %}", but I'd like to send the selected value (from any select inputs that has been selected) to this action url, as it would be {% url 'solution_product_list' selected_value %}. I did some searches and came accross to these questions which suggested using redirect:

Django form action url dynamic

How to send selected option value in form action in django

Django : HTML form action directing to view (or url?) with 2 arguments

Dynamic URL routing from html form within action in Django

But none the above helped me, or maybe I could not understand the solutions there. Can anyone please help me on this matter?

Also I have a messy view for this page, as below:

def solution_product_list(request):
    product_list_queryset = None
    sub_usage_list = []
    pump_type_list = []
    if request.method == "POST":
        if request.POST["usage_select"] != "----":
            usage_select = request.POST["usage_select"]
            product_list_queryset = Product.objects.filter(usage__id=usage_select)
            sub_usage_list = SubUsage.objects.filter(usage__id=usage_select)
            if request.POST["sub_usage_select"] != "همه‌ی کابردهای جزئی":
                sub_usage_select = request.POST["sub_usage_select"]
                product_list_queryset = Product.objects.filter(
                    sub_usage__id=sub_usage_select
                )
        elif request.POST["model_select"] != "----":
            model_select = request.POST["model_select"]
            product_list_queryset = Product.objects.filter(main_model__id=model_select)
            pump_type_list = PumpType.objects.filter(
                main_model__id=model_select
            )
            if request.POST["pump_type_select"] != "همه‌ی تیپ‌های این مدل":
                pump_type_select = request.POST["pump_type_select"]
                product_list_queryset = Product.objects.filter(
                    pump_type__id=pump_type_select
                )
        elif request.POST["head"] and request.POST["flow"]:
            flow = float(request.POST["flow"])
            head = float(request.POST["head"])
            head_flow_list_main = get_head_flow_list(product_list_queryset)
            product_list = return_filtered_products(
                head_flow_list_main, head, flow
            )
            product_list_queryset = Product.objects.filter(id__in=product_list)
    page = request.GET.get("page", 1)
    paginator = Paginator(product_list_queryset, 10)
    try:
        product_list = paginator.page(page)
    except PageNotAnInteger:
        product_list = paginator.page(1)
    except EmptyPage:
        product_list = paginator.page(paginator.num_pages)

    queryset_dictionary = get_common_queryset()
    page_context = {
        "product_list": product_list_queryset,
        "sub_usage_queryset": sub_usage_list,
        "pump_type_queryset": pump_type_list,
    }
    print(product_list_queryset)
    context = {
        **queryset_dictionary,
        **page_context,
    }
    return render(request, "solutions/second_stage_select.html", context)

I'm relatively new to Django, so if you can give me any advice on better code formatting or writing, your notes will be most appreciated.

Vahid
  • 249
  • 4
  • 12
  • I think this post solution contains your answer too: https://stackoverflow.com/questions/72540076/how-to-display-selected-option-value-selected-in-option-tag-in-django-template-f – Seyed Javad Adabikhosh Dec 24 '22 at 11:12
  • @SeyedJavadAdabikhosh I can't understand how it is related to my problem, can you elaborate please? – Vahid Dec 24 '22 at 11:16

2 Answers2

1

Although it's better to use form widgets to manage this situations but another solution is: selected_lang = request.POST.get('lang_txt') So your code in view could be as follow: usage_select = request.POST.get('usage_select')

And in your html template you can do as follow to maintain and display previos selected options using your context:

<option value="span_to_eng" {% if data.selected_lang == 'span_to_eng' %}selected{% endif %}>Spanish To English</option>
1

The reason is, you set the values of the context when there is a post method, but you do not return an i.e. HttpResponse or redirect object.

let's solve your problem with less code, you can get the idea:

views.py:

from django.shortcuts import render
from django.shortcuts import redirect

def first_stage(request):
    extra_context_from_view = ["opt1", "opt2", "opt3"]

    context = {
        "extra_context_from_view": extra_context_from_view,
        "previously_saved_val_db": "opt2",
    }
    if request.method == "POST":
        if request.POST["first_stage"]:
            context = {"forward_to_second_stage": request.POST["first_stage"]}
        # in the case of redirect uncomment two below line 
        # and you should comment the second_stage render 
        # request.session['redirected_context'] = {'redirected': 'test'}
        # return redirect("second_stage")
        return render(request, "pages/second_stage.html", context)
    return render(request, "pages/first_stage.html", context)

def second_stage(request):
    context = {"second_view": "from_second_view"}
    return render(request, "pages/second_stage.html", context)

first_stage.html

{% comment %}
remove this line if it is not necessary
{% if val == previously_saved_val_db %}selected{% endif %} 

it is just an example to show you how to dynamically set the
value attribute of a tag in HTML.
{% endcomment %}

<form method="POST" class="row" action="{% url 'first_stage' %}">
    {% csrf_token %}
    <h2 class="h5 nm-text-color fw-bold mb-4">Some Header</h2>
    <select name="first_stage">
        <option selected>----</option>
        {% for val in extra_context_from_view %}
            <option value="{{ val }}"
            {% if val == previously_saved_val_db %}selected{% endif %}>{{ val }}</option>
        {% endfor %}
    </select>
    <input type="submit" value="submit" id="submit">
</form>

second_stage.html

<p> Normal view after post at the same URL (view)</p>
<h1> YOUR SELECT OPTION: {{ forward_to_second_stage }} </h1>

<p> Redirected version at another URL (view)</p>

{% comment %}
pay atention you probably get session value even you do not use 
the redirect method 
{% endcomment %}
{{ request.session.redirected_context }}
<br>
{{ second_view }}

urls.py:

urlpatterns = [
    path("first-stage/", views.first_stage, name="first_stage"),
    path("second-stage/", views.second_stage, name="second_stage"),
]

if you use the return render(request, "pages/second_stage.html", context) the url will not change and the process will be performed at the current view, but with a different template.

P.S:

  • Post your question with a minimal reproducible example.
  • You can debug your code by printing inside the template.
  • You can see what values are passed from the view to the template and vice versa.
  • Use something like IntegerChoices or TextChoices in your model and, use them inside the view for comparison.
  • Use {% trans %} like tag in template for the labels.

EDIT

When the request method is post in your code, and the user submits the form, what does happen?

def solution_product_list(request):

    if request.method == "POST":
        if request.POST["usage_select"] != "----":
            # some logic
        elif request.POST["model_select"] != "----":
            # some logic
        elif request.POST["head"] and request.POST["flow"]:
            # some logic
        # ok then ???
    # pagination

    # other logic and updating context
    # do you see in this way the updated context at second_stage_select.html?
    
    return render(request, "solutions/second_stage_select.html", context)

Now, you can compare my answer views.py and yours. If you comment all the return objects inside the if request.method == "POST": it would be the same as your code. So, the rest of the explanation is extra!

  1. What is the extra_context_from_view?

I do not have access to your models, and it is time consuming to reproduce (if it would be possible even) your problem exactly. So, this value create options for the select tag and I am passing it to the template to simulate your problem. You can consider it as "product_list" in page_context = {"product_list": product_list_queryset, ...}.

  1. What is "previously_saved_val_db" and how did you set opt2 for it?

Again, it is another context value, to show, how you can use if/else inside the template, and set the selected attribute of select tag. I used this name previously_saved_val_db , because it gives the sense that, if previously, for one of your models object, you have already a value in db, and you want to set it for select tag automatically. Do I have a db for your question? No! I use an example value so.

  1. Does request.POST["first_stage"] return the value of this select tag? Yes, as I told before you can use print to debug the code. Use print(request.POST["first_stage"]) in the views.py and {{ forward_to_second_stage }} in the template to see them.

  2. What does the line context = {"second_view": "from_second_view"} mean? It was another example, to show, that the second_stage.html and its view function are working. You can comment the second_stage function and its path in urls.py, you will see that, this time there is no {"second_view": "from_second_view"} in the rendered result after post`

before post, render version , redirect version.

P.S (2). Pay attention, if you want to pass the select option with redirect, you should first add them inside the view, and then use it insde the template, look how I did that for request.session['redirected_context'] = {'redirected': 'test'}.

I hope now, it is clear enough!

A D
  • 585
  • 1
  • 7
  • 16
  • Thank you for your answer, but after even a few tries, I couldn't understand your code. I'm trying to understand and use it, but please clarify a bit more if possible. My questions are: 1- what is `extra_context_from_view`? 2- what is `"previously_saved_val_db"` and how did you set `opt2` for it? 3- Does `request.POST["first_stage"]` return the value of this select tag? 4- What does the line `context = {"second_view": "from_second_view"}` mean? – Vahid Dec 25 '22 at 05:08
  • 1
    just a simple question, did you run my code in your local dev environment? you just need to copy/paste everything and put the template files in a correct folder. your question by the way shows miss understanding the basics. i edit my answer to explain more! – A D Dec 25 '22 at 08:28
  • 1
    @A D Nope, I did not run your code because I didn't understand it. And I intend to learn from you so it would be perfect if you could answer the questions above. Thank you for the time and effort you are giving me. – Vahid Dec 25 '22 at 08:33
  • @A D inorder to use your solution, I'm stuck in the next step :(, where I've asked another question for it. Could you please take a look at that question too? https://stackoverflow.com/questions/74912100/set-dynamic-values-from-a-dictionary-for-select-values-inside-a-django-template – Vahid Dec 25 '22 at 10:19