Laur Ivan
Laur Ivan

Reputation: 4177

Get the clicked submit button in a django form in a generic way

I have a finite state machine (django-fsm) which allows an object to go from a source state into one of several target states. I can add all the actions in a dictionary like:

ACTIONS { 'button_1': action1,
          'button_2': action2,
           ...
}

This translates in a form with a submit button for each state.

  {% for n,m in object.get_available_current_state_transitions %}
      <input type="submit" class="btn" value="{{ n|get_action|capfirst }}"
             name="button_{{n}}" />
  {%endfor%}

  <input type="submit" class="btn primary" value="Save">
  <a class="btn" onclick="javascript:history.go(-1)">Cancel</a>

This usually results in more than 3 buttons.

Clicking a button results in a specific action, defined in my case in the model class.

Now, I know I can get the clicked button in the request.POST dictionary, but this would result in a cascade if like:

  if 'button_1' in request.POST:
      action_1()
  elif 'button_2' in request.POST:
      ...

Is there any way to get the button pressed separately (ideally from the request object) in a variable so I can have something like

ACTIONS[clicked_button_name](...)

? In other words, is there any way to obtain the clicked button outside the POST dictionary?

PS: I've looked other replies on the "multiple buttons" question, but all offer request.POST as answer.

Upvotes: 0

Views: 2962

Answers (2)

If all of the actions and strings are already in your view, why don't you just iterate over that actions dict?

for key, value in ACTIONS.items():
    if key in request.POST:
        value()

Just make your button names very unlikely to be used as a regular form field name.


A few alternatives: use javascript to handle the submission and have it set a single form field such as "action".

Use more unique keys and filter through request.POST.keys() with a regex pattern or string comparison.

action = [x for x in request.POST.keys() if 'FAIRLY_UNIQUE_BUTTON_PREFIX' in x]

if action:
    ACTIONS[action]()

Upvotes: 2

James R
James R

Reputation: 4656

I don't think so directly, but a couple workarounds could be:

  1. Send your buttons to different urlconfs with some variable (like a three letter arg). All of these confs point to the same view taking this three letter arg as an argument, which then knows what to do. This might still result in a cascade if else though.

  2. Or, send them to different views all together.

  3. You could try doing something ajaxy. The data will still be in a post dict, but you will have more control over how the post dict is structured.

I'm also assuming GET isn't an option for any of these (yet that still results in if else structures.)

Upvotes: 1

Related Questions