Reputation: 14290
I have Django 1.6 application that will contain a form that displays a list of customers with a pair of "Approve" and "Reject" radio buttons next to each name to indicate if we will approve or reject their method of payment. I want each radio button to be set by default to "Reject" when the form is first rendered. I also want to include a hidden "uid" field on each line that contains the customer's user ID. When the admin clicks the Approve button next to each username s/he wants to approve and then submits the form, the view should read each hidden id value for each user, inspect the radio button, and update the the model if the user is approved. Here's what the form will look like:
customer1 (hidden id) [ ] approve [x] reject
customer2 (hidden id) [ ] approve [x] reject
...
customerN (hidden id) [ ] approve [x] reject
I have three problems that I don't quite understand how to solve:
I'm really having a hard time wrapping my head around these tasks. Thanks very much for your help.
# views.py
def review_payment_methods(request, template):
if request.method == "POST":
payment_method_form = ReviewPaymentMethodForm(request.POST)
if payment_method_form.is_valid():
# How to iterate through form and pull out ids and radio button values??
# Update Account table here
return HttpResponseRedirect('/admin/')
else:
new_accounts = Account.objects.filter(method_approved=False).values()
PaymentMethodFormset = formset_factory(ReviewPaymentMethodForm, extra=new_accounts.count())
formset = PaymentMethodFormset(initial=new_accounts) # This doesn't seem to work
return render_to_response(template, locals(), context_instance=RequestContext(request))
# models.py
class Account(models.Model):
"""A user's account."""
user = models.OneToOneField(User, primary_key=True, unique=True)
method_approved = models.BooleanField(default=False) # This contains Approve/Reject
# forms.py
from django import forms
from django.utils.safestring import mark_safe
class ReviewPaymentMethodForm(forms.Form):
class HorizontalRadioRenderer(forms.RadioSelect.renderer):
def render(self):
return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))
DECISION_CHOICES = (('1', 'Approve'), ('2', 'Reject'))
uid = forms.IntegerField(widget=forms.HiddenInput)
decision = forms.ChoiceField(
choices = DECISION_CHOICES,
widget = forms.RadioSelect(renderer=HorizontalRadioRenderer),
initial = '2', # 1 => Approve, 2 => Reject
)
# review_payment_methods.html
<div class="custom-content">
<h1>Review Payment Methods</h1>
<form action="." method="post">{% csrf_token %}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
<input type="submit" value="Submit" />
</form>
</div>
Upvotes: 0
Views: 1344
Reputation: 599956
This isn't really a job for formsets. You want a single form with a dynamic set of fields; each field's name is the customer UID and its value is accept or reject. To do that, you can create the fields programmatically when instantiating the form:
class ReviewPaymentMethodForm(forms.Form):
def __init__(self, *args, **kwargs):
accounts = kwargs.pop('accounts')
super(ReviewPaymentMethodForm, self).__init__(*args, **kwargs)
for account in accounts:
self.fields[str(account.id)] = forms.ChoiceField(
label=account.user.username,
choices=DECISION_CHOICES,
widget=forms.RadioSelect(renderer=HorizontalRadioRenderer),
initial='2', # 1 => Approve, 2 => Reject
)
And the view becomes:
def review_payment_methods(request, template):
new_accounts = Account.objects.filter(method_approved=False)
if request.method == "POST":
payment_method_form = ReviewPaymentMethodForm(request.POST, accounts=new_accounts)
if payment_method_form.is_valid():
for acc_id, value in payment_method_form.cleaned_data.items():
approved = (value == '1')
Account.objects.filter(pk=acc_id).update(method_approved=approved)
return HttpResponseRedirect('/admin/')
else:
form = ReviewPaymentMethodForm(accounts=new_accounts)
return render(request, template, {'form': form})
Upvotes: 2