How to render form with errors in generic detail view - django

I am trying to render a generic detail view page with a form with errors
My post method in my generic detail view is
def post(self, request, slug):
if 'submit-edit-roster' in request.POST:
edit_roster_form = EditRosterForm(request.POST, team=self.request.user.playerprofile.team)
if edit_roster_form.is_valid():
edit_roster_form.save()
return redirect ('tcl-team', slug=self.request.user.playerprofile.team.urlslug)
my edit roster form is
class EditRosterForm(forms.Form):
members = 0
team = None
sublist = []
playerlist = []
def __init__(self, *args, **kwargs):
self.team = kwargs.pop('team', None)
self.members = 0
self.sublist = []
self.playerlist = []
super(EditRosterForm, self).__init__(*args, **kwargs)
currentroster = Roster.objects.filter(team=self.team)[0]
for member in Playerprofile.objects.filter(team=self.team).order_by('name'):
if member in currentroster.players.all():
self.fields[str(member.name)] = forms.ChoiceField(choices=ROSTER_CHOICES)
self.initial[str(member.name)] = '1'
elif member in currentroster.subs.all():
self.fields[str(member.name)] = forms.ChoiceField(choices=ROSTER_CHOICES)
self.initial[str(member.name)] = '2'
self.members += 1
def clean(self):
cleaned_data = super().clean()
i = 0
for member in Playerprofile.objects.filter(team=self.team):
if cleaned_data[member.name] == '1':
self.playerlist.append(member)
elif cleaned_data[member.name] == '2':
self.sublist.append(member)
i += 1
print(len(self.sublist))
if len(self.sublist) > 2:
raise ValidationError("Maximum of 2 subs allowed")
if len(self.playerlist) > 5:
raise ValidationError("Maximum of 5 players allowed")
if len(self.playerlist) + len(self.sublist) > i:
raise ValidationError("Team members must be a sub or a player")
return cleaned_data
def save(self):
cleaned_data = self.cleaned_data
print(cleaned_data)
UpdateRoster(roster=self.team.GetCurrentRoster(), players=self.playerlist, subs=self.sublist)
When my form has errors I get
The view team.views.TeamEditView didn't return an HttpResponse object. It returned None instead.
I know I need to add something to pass my form the form object with errors but can't find how to do this.
if edit_roster_form.is_valid():
edit_roster_form.save()
return redirect ('tcl-team', slug=self.request.user.playerprofile.team.urlslug)
else:
# Render detail view page with form
Apologies if my question is incoherent, I'm new to django and stackoverflow

I think you have to add request as parameter to the render shortcut

Related

Django. Pass variable from view to template

I want to pass variable appuser to template and I don't understand how to do it.
I have tried to use kwargs.update but it still doesn't work.
I have a view:
class CausesView(AjaxFormView):
appuser = None
causes = []
cause_allocation_set = None
def prepare_request(self, request, *args, **kwargs):
self.causes = Cause.objects.filter(is_main_cause = True)
self.appuser = AppUser.get_login_user(request)
self.cause_allocation_set = set([r.cause_id for r in self.appuser.current_cause_save_point.cause_allocations_list])
def prepare_context(self, request, context, initial):
initial.update(
causes = self.cause_allocation_set,
appuser = self.appuser,
)
def prepare_form(self, request, form):
form._set_choices("causes", [(r.id, r.title) for r in self.causes])
def custom_context_data(self, request, **kwargs):
kwargs.update(
special_test = "dsf"
)
return kwargs
def process_form(self, request, form):
data = form.cleaned_data
try:
with transaction.atomic():
if self.cause_allocation_set != set(data.get('causes')):
self.appuser.save_causes(data.get('causes'))
except Exception as e:
message = "There was an error with saving the data: " + str(e.args)
return AjaxErrorResponse({'title':"Error", 'message':message})
return AjaxSuccessResponse('Causes Saved')
And I have a form:
class CauseForm(AjaxForm):
causes = forms.TypedMultipleChoiceField(label="Select Causes", choices = (), required = False, coerce = int,
widget = forms.CheckboxSelectMultiple())
def clean(self):
cleaned_data = super(CauseForm, self).clean()
causes = cleaned_data.get('causes')
validation_errors = []
if not causes is None and not len(causes):
validation_errors.append(forms.ValidationError("At least one Cause is required"))
if len(validation_errors):
raise forms.ValidationError(validation_errors)
return cleaned_data
How can I get variable appuser in temlpate?
For example:
{{ appuser.name }}
doesn't work.
Read How to use get_context_data in django
and
https://docs.djangoproject.com/en/1.9/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_context_data
Here is example of how you can do this
class CausesView(AjaxFormView):
...
def get_context_data(self, **kwargs):
context_data = super(CausesView, self).get_context_data(**kwargs)
context_data['appuser'] = self.appuser
return context_data

Access missing value in form.cleaned_data

I was trying to dynamically generate fields as shown in http://jacobian.org/writing/dynamic-form-generation/. My case slightly differs in that I am looking to use multiplechoicefield that is dynamically created. This is what I came up with...
views.py
def browseget(request):
success = False
if request.method == 'POST':
list_form = ListForm(request.POST)
if list_form.is_valid():
success = True
path = list_form.cleaned_data['path']
minimum_size = list_form.cleaned_data['minimum_size']
follow_link = list_form.cleaned_data['follow_link']
checkboxes = list_form.cleaned_data['checkboxes']
....do something
else:
list_form = ListForm(name_list)
ctx = {'success': success, 'list_form': list_form, 'path': path, 'minimum_size': minimum_size}
return render_to_response('photoget/browseget.html', ctx, context_instance=RequestContext(request))
forms.py
class ListForm(forms.Form):
path = forms.CharField(required=False)
minimum_size = forms.ChoiceField(choices=size_choices)
follow_link = forms.BooleanField(required=False, initial=True)
def __init__(self, *args, **kwargs):
name_list = kwargs.pop('name_list', None)
super(ListForm, self).__init__(*args, **kwargs)
print 'Received data:', self.data
if name_list:
name_choices = [(u, u) for u in name_list]
self.fields['checkboxes'] = forms.MultipleChoiceField(required=False, label='Select Name(s):', widget=forms.CheckboxSelectMultiple(), choices=name_choices)
def clean_path(self):
cd = self.cleaned_data
path = cd.get('path')
if path == '': path = None
return path
def clean_minimum_size(self):
cd = self.cleaned_data
minimum_size = cd.get('minimum_size')
if minimum_size is None: minimum_size = 0
return int(minimum_size)
The form generates and displays perfectly... until I post some data. The 'checkboxes' field doesn't show up in list_form.cleaned_data.items() while it shows in self.data. As it is the form breaks with a KeyError exception. So Im asking, how do i access the checkboxes data?
You're not passing in the name_list parameter when you re-instantiate the form on POST, so the field is not created because if name_list is False.

unsupported operand type(s) for ** or pow(): 'tuple' and 'dict'

I'm following this tutorial with a few changes: http://www.jacobian.org/writing/dynamic-form-generation/, all of which I'll post as snippets below. When I attempt to access a page that goes through my dynamic form view it throws a TypeError. Here's the traceback: http://dpaste.com/793196/.
forms.py (testing/forms.py)
from django import forms
class TestForm(forms.Form):
test_id = forms.CharField()
user_id = forms.CharField()
complete_time = forms.IntegerField()
def __init__(self, *args, **kwargs):
extra = kwargs.pop('extra')
super(TestForm, self).__init__(*args **kwargs)
for i, question in enumerate(extra):
self.fields['custom_%s' % i] = forms.CharField(label=question)
def extra_answers(self):
for name, value in self.cleaned_data.items():
if name.startswith('custom_'):
yield (self.fields[name].label, value)
views.py (testing/views.py)
def exam(request, test_id):
user = request.user
t = get_object_or_404(Test, pk=test_id)
if user.is_authenticated():
extra_questions = get_questions(request, test_id)
if request.method == 'POST':
form = TestForm(request.POST or None, extra=extra_questions)
if form.is_valid():
for (question, answer) in form.extra_answers():
save_answer(request, question, answer)
return HttpResponseRedirect('/tests/')
else:
form = TestForm(None, extra=extra_questions)
return render(request, 'testing/exam.html', { 't' : t, 'form' : form })
else:
return HttpResponseRedirect('/')
def get_questions(request, test_id):
t = get_object_or_404(Test, pk=test_id)
questions = t.question_set.all()
for question in questions:
title = question.question
qlist = []
qlist.append(title)
Any help would be appreciated as I'm racking my mind for an answer.
You accidentally forgot the comma.
super(TestForm, self).__init__(*args, **kwargs)

Overriding save() model method for permissions

Am trying to override save() method so that only the creator and administrator are able to update the model field values as :
if not self.pk:
super(Shastra, self).save(*args, **kwargs)
else:
if (self.person == args[0].user) or (self.person.is_superuser):
super(Shastra, self).save(*args, **kwargs)
While update am passing request to the save method as
def edit(request, shastra_id):
shastra_id = int(shastra_id)
shastra = Shastra.objects.get(pk = shastra_id )
if request.method == 'POST':
form_shastra_edit = ShastraEditForm(request.POST, instance = shastra)
if form_shastra_edit.is_valid():
form_shastra_edit.save(request)
return HttpResponseRedirect('/edited/successfully')
else:
form_shastra_edit = ShastraEditForm(instance = shastra)
But am getting " tuple index out of range " error . What is going wrong in this ?
You can not use request in a Model.save() method. You have to do request-based validation in your views method (in your edit method for instance). The Model layer is unaware of 'request' objects..
Make your edit method something like:
def edit(request, shastra_id):
shastra_id = int(shastra_id)
shastra = Shastra.objects.get(pk = shastra_id )
if request.method == 'POST':
form_shastra_edit = ShastraEditForm(request.POST, instance = shastra)
if form_shastra_edit.is_valid() and shastra.user == request.user:
form_shastra_edit.save(request)
return HttpResponseRedirect('/edited/successfully')
else:
form_shastra_edit = ShastraEditForm(instance = shastra)

Accessing two returned values in Django view

I have my form clean method return two values. How do I distinguish the two variables in my view. Basically, I want to use the form data to check the database and return an object if it exists so that I can pass it to a new view. My goal is to not hit the database twice, once to see if the object exists and another time to retrieve it to display to the user.
Forms.py
class DocumentCodeLookup(forms.Form):
code = forms.CharField(max_length=15, error_messages={'required': 'Whoops! Please enter the Document Code from your ticket.'})
def clean_code(self):
code = self.cleaned_data['code'].upper()
if (re.match(r'^[A-Z0-9]{4,8}[-][A-Z0-9]{6}$',code)):
code_parts = code.split('-')
try:
q = Code.objects.get( user_defined_code__name=code_parts[0], document_code=code_parts[1] )
except Code.DoesNotExist:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
else:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
return code, q
Views.py
def index(request):
code_lookup_form = DocumentCodeLookup()
if request.method == 'POST':
code_lookup_form = DocumentCodeLookup(request.POST)
if code_lookup_form.is_valid:
redirect('document', x = q) # I want to pass the returned object to the view
return render_to_response('base/splash_page.html' ,{
'code_lookup_form' : code_lookup_form
}, context_instance=RequestContext(request))
Will clean_field even work like that?
http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
Note the comment.
You can put the attr on the form with self.
class DocumentCodeLookup(forms.Form):
code = forms.CharField(max_length=15, error_messages={'required': 'Whoops! Please enter the Document Code from your ticket.'})
def clean_code(self):
code = self.cleaned_data['code'].upper()
if (re.match(r'^[A-Z0-9]{4,8}[-][A-Z0-9]{6}$',code)):
code_parts = code.split('-')
self.q = None
try:
self.q = Code.objects.get( user_defined_code__name=code_parts[0], document_code=code_parts[1] )
except Code.DoesNotExist:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
else:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
return code
q is on the form.
def index(request):
code_lookup_form = DocumentCodeLookup()
if request.method == 'POST':
code_lookup_form = DocumentCodeLookup(request.POST)
if code_lookup_form.is_valid():
redirect('document', x = code_lookup_form.q) # <---
return render_to_response('base/splash_page.html' ,{
'code_lookup_form' : code_lookup_form
}, context_instance=RequestContext(request))