2
\$\begingroup\$

I have made a ChoiceField into a select and submit button and wondered if this is the correct approach.

forms.py

class PortfolioForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super(PortfolioForm, self).__init__(*args, **kwargs)

        portfolios = [('pf 1', 'pf 1'), ('pf 2', 'pf 2'), ('pf 3', 'pf 3')]

        self.fields['portfolios'] = forms.ChoiceField(
                    widget=forms.Select(),
                    choices=portfolios,
                    required=False,
                    )

views.py

class PortfolioView(View):

    form_class = PortfolioForm
    template_name = 'portfolio.html'

    def get(self, request):
        form = self.form_class()

        return render(request, self.template_name, {'form': form})

    def post(self, request):
        form = self.form_class(self.request.POST)

        if form.is_valid():
            selected_portfolio = form.cleaned_data.get('portfolios')
            print(selected_portfolio) 
            form = self.form_class(initial={'portfolios': selected_portfolio})

        else:
            form = self.form_class(initial={'portfolio_name': ''})

        return render(self.request, self.template_name, {'form': form})

and most of the trickery in the template. I use elements of form.portfolios to make the buttons, but it seems I have to include form.portfolios in the template to make it all work. In order not to show I put this in a hidden tag.

{% block content %}
  <form id="id_portfolio" method="post">
    {% csrf_token %}
    <a style="visibility: hidden">{{ form.portfolios }}</a>
    {% for portfolio in form.portfolios %}
      <p>
      {% if portfolio.data.selected %}
        <button type="button">{{ portfolio.choice_label }}</button>

      {% else %}
        <button type="submit" value="{{ portfolio.data.value }}" 
          name="{{ portfolio.data.name }}">{{ portfolio.choice_label }}</button>

      {% endif %}
      </p>
    {% endfor %}
{% endblock content %}
\$\endgroup\$
  • \$\begingroup\$ I haven't tried replicating this on my own machine, but what goes terribly wrong if you just use Django's built-in form rendering using {{ form }}? \$\endgroup\$ – scnerd Sep 10 '19 at 15:20
  • \$\begingroup\$ Just rendering the built-in form {{ form.portfolios }} gives me a dropdown list. I want the buttons to be visible at all time. \$\endgroup\$ – Bruno Vermeulen Sep 11 '19 at 3:41
  • \$\begingroup\$ Do you want there to be a button for each portfolio choice on the page? It may be better to override the Select widget and replace the select.html template with a template that renders buttons instead of a dropdown select \$\endgroup\$ – Mikey Lockwood Sep 11 '19 at 8:35
  • \$\begingroup\$ @mikeyl, yes correct I do not want the dropdown but still want to use the choicefied. Can you point me to how to replace the select.html with one that renders buttons. \$\endgroup\$ – Bruno Vermeulen Sep 11 '19 at 9:21
2
\$\begingroup\$

If you want the options to appear on the page, I would recommend using the RadioSelect widget instead. You will still need to click a separate button to submit however.

class PortfolioForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super(PortfolioForm, self).__init__(*args, **kwargs)

        portfolios = [('pf 1', 'pf 1'), ('pf 2', 'pf 2'), ('pf 3', 'pf 3')]

        self.fields['portfolios'] = forms.ChoiceField(
                    widget=forms.RadioSelect(),
                    choices=portfolios,
                    required=False,
                    )

If you would like your own custom widget instead, you can inherit one of the existing widgets and apply your own template.

# widgets.py
class ButtonSelect(ChoiceWidget):
    template_name = 'widgets/button_select.html'
    option_template_name = 'widgets/button_select_option.html'


# templates/widgets/button_select.html
{% include "django/forms/widgets/multiple_input.html" %}

# templates/widgets/button_select_option.html
""""
Will need custom widget option code here.
Take a look at https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/input_option.html
"""

Then you can use it in your PortfolioForm

from widgets import ButtonSelect

class PortfolioForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super(PortfolioForm, self).__init__(*args, **kwargs)

        portfolios = [('pf 1', 'pf 1'), ('pf 2', 'pf 2'), ('pf 3', 'pf 3')]

        self.fields['portfolios'] = forms.ChoiceField(
                    widget=ButtonSelect(),
                    choices=portfolios,
                    required=False,
                    )
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.