Django forms "pattern" for this situation? - django

I'm pretty new to Python so that may be a stupid question but I'll ask it anyway. Is there a Django forms "design pattern" for this common view situation? When I run the view, I want it to act on one of two different types of forms depending on the type of user who's filling out the form. It seems ugly to have two if/then blocks inside the if request.method block to determine which type of form I'm acting on. What I'd like is to be able to refer to a "CreateProfileForm" that will refer to either a CreateManProfileForm or CreateWomanProfileForm depending on what's in the session variable.
Thanks!
def create_profile(request, template):
if request.session['user_type_cd'] == 'man':
is_man = True
else:
is_man = False
if request.method == "POST":
if is_man:
form = CreateManProfileForm(request.POST)
else:
form = CreateWomanProfileForm(request.POST)
if form.is_valid():
# Do stuff
return HttpResponseRedirect(reverse('do-next-thing'))
else:
if is_man:
form = CreateManProfileForm()
else:
form = CreateWomanProfileForm()
return render_to_response(template, locals(), context_instance=RequestContext(request))

You can do something like this:
Create a dictionary of the forms,
FORMS = {
0: CreateWomanProfileForm,
1: CreateManProfileForm
}
And in the views:
def create_profile(request, template):
is_man = 1 if request.session.get('user_type_cd') == 'man' else 0
if request.method == "POST":
form = FORMS.get(is_man)(request.POST)
if form.is_valid():
# Do stuff
return HttpResponseRedirect(reverse('do-next-thing'))
else:
form = FORMS.get(is_man)()
return render_to_response(template, locals(), context_instance=RequestContext(request))
Or even this should work
def create_profile(request, template):
is_man = 1 if request.session['user_type_cd'] == 'man' else 0
form = FORMS.get(is_man)(request.POST or None)
if request.method == "POST":
if form.is_valid():
# Do stuff
return HttpResponseRedirect(reverse('do-next-thing'))
return render_to_response(template, locals(), context_instance=RequestContext(request))

Related

Django post-form cannot validate when using Form with additional inputs

I have a form containing af MultipleChoiceField where the choices are created dynamic based on the given user
class UpdateForm(forms.Form):
def __init__(self,names,*args,**kwargs):
super(UpdateForm,self).__init__(*args,**kwargs)
self.fields["list_names"] = forms.MultipleChoiceField(choices = zip(names,names),widget=forms.CheckboxSelectMultiple,label="Pick some names")
add_new = forms.BooleanField(initial=True, label="Add new names?",required=False)
delete_missing = forms.BooleanField(label = "Delete names?",required=False)
and it works fine as GET-request, the issues arrives with the post-request:
My view is the following:
def update(request):
user = request.user
list_names = MyModel.objects.filter(user=user).all().values_list("nick_name",flat=True).distinct()
form = UpdateWishlistForm(names =list_names)
if request.method == "POST":
post_form = UpdateForm(request.POST)
if post_form.is_valid():
list_names = post_form.cleaned_data["list_names"]
add_new = post_form.cleaned_data["add_new"]
delete_missing = post_form.cleaned_data["delete_missing"]
messages.success(request, "Success")
context = {
"form":form,
}
redirect("home")
else:
#invalid post_form
messages.error(request, "Error")
context = {
"form":form,
}
return render(request, "discounttracker/update.html")
else: #Get request
context = {
"form":form,
}
return render(request, "myapp/update.html",context=context)
The post_form = UpdateForm(request.POST) does not validate and the post_form.errors is empty.
It does contain data though (before calling post_form.is_valid())
print(post_form)
# UpdateForm: <UpdateForm bound=False, valid=Unknown, fields=(add_new;delete_missing;list_names)>
print(request.POST.dict())
#<QueryDict: {'csrfmiddlewaretoken': ['...'], 'add_new': ['on'], 'list_names': ['test_name_1']}>
but I notice it is not bound, thus not validating. But I cannot understand why it's not "binding" when parsing request.POST?
In the POST request, you need to pass the names as well, so:
list_names = MyModel.objects.filter(user=user).values_list("nick_name",flat=True).distinct()
form = UpdateWishlistForm(names=list_names)
if request.method == 'POST':
post_form = UpdateForm(names=list_names, data=request.POST)
# …
# …
But I would advise to work with a ModelMultipleChoiceField [Django-doc] and thus pass a queryset. Since the nick names apparently can contain duplicates, it might be better to make a Nickname model, and use ForeignKeys to that model.

Save and Continue in Django Forms

I have created a django form which at the moment I can only save the POST. Would like to add Save and Add another button in my templates and views function. Has anyone found a solutions.
if request.method == "POST":
form = StktxnsForm(request.POST )
if form.is_valid():
new_txns = form.save(commit=False)
new_txns.created_by = request.user
new_txns.save()
return redirect('pending_transactions')
else:
form = StktxnsForm()
return render(request,'new_transaction.html', {'form': form})
You may think use AJAX function to save and continue without reload. Send 'POST' request to save data in Ajax. It will help you.
I used like this in class-based view (vanilla method)
class PackageCreateView(View):
def get(self,request,*args,**kwargs):
return render(request,'package/create_package.html')
def post(self,request,*args,**kwargs):
if request.user.is_authenticated:
if request.method == 'POST':
data = request.POST
name = data.get('name')
detail = data.get('des')
price = data.get('price')
fname = Package.objects.all().filter(name=name)
if fname:
messages.info (request,'sorry name already exits')
return redirect ('create_package')
elif request.POST.get ('continue') :
pac = Package(name=name, detail=detail, price=price)
pac.save()
return redirect('create_package')
else:
pac = Package(name=name, detail=detail, price=price)
pac.save()
return redirect('packagelist')
else:
return redirect ('create_package')
else:
return redirect('login')
Here's one way to do it.
On your template:
<button type="submit" name="save_add" value="True">Save & Add another</button>
In your view:
if form.is_valid():
new_txns = form.save(commit=False)
new_txns.created_by = request.user
new_txns.save()
if request.POST.get('save_add'):
return redirect('create_transaction')
return redirect('pending_transactions')

invalid syntax in view.py in django.?

I m facing error in Django :invalid syntax (views.py).
def deals(request):
form = deals()
if request.method == "POST":
form = deals(request.POST, request.FILES)
if form.is_valid():
form.save()
else:
return render_to_response("deals.html", {'form':form}, context_instance=RequestContext(request))
else:
form = deals()
return render_to_response("deals.html", {'form':form}, context_instance=RequestContext(request))
The problem is actually an issue with indentation, which manifests as a syntax error. The four lines beginning if form.is_valid() should be indented one level.
However, this would still not be the recommended pattern. You don't need the inner else at all, and you must redirect after a successful post.
def deals(request):
form = DealsForm()
if request.method == "POST":
form = DealsForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('/') # or wherever
else:
form = DealsForm()
return render(request "deals.html", {'form':form})
Also I've used the render shortcut instead of render_to_response, as that uses a RequestContext automatically.
Note that all this is explicitly given in the docs; there's no reason to do anything else.

The view didn't return an HttpResponse object error when form is not valid: django

I've used Django forms. I have this function in views.py:
def func(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
//do processing
return HttpResponseRedirect('/')
else:
form = MyForm()
return render_to_response("checkbox.html", RequestContext(request, {'form':form}))
but when form is invalid, it shows me the error: The view didn't return an HttpResponse object. I've searched and realized every where the view functions are like this, but I don't know why mine has error. It seems it doesn't know what to do, while form in invalid!!! Why it doesn't show the page and show user the form errors? can you please help me?
When the form is invalid, the view just returns since else part of the if statement is only evaluated when the request.method == "POST" is False, which it is not...
To fix this, the following is the usual pattern for making form views:
def func(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
//do processing
return HttpResponseRedirect('/')
else:
form = MyForm()
# outside of the else clause
# if the form is invalid, then it will also show the error messages to the user
return render_to_response("checkbox.html", RequestContext(request, {'form':form}))
You already have your answer on #miki725 post. Just a suggestion you might want to consider GET as the default behaviour to avoid those if .. else:
def func(request):
# GET is the default behaviour
form = MyForm()
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
//do processing
return HttpResponseRedirect('/')
return render_to_response("checkbox.html", RequestContext(request, {'form':form}))

django - how to check if model is empty

I have settings form page. If user filled the form once; it must display those values. But if there is no data [first time] I get query error. I need that query, because the form data must be written as related with current user [logged in].
here is my view part :
#login_required(login_url='/login/')
def profile_page(request,username):
query = Profile.objects.get(owner__username = username) ##error!
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},context_instance = RequestContext(request))
I think I need to check the model and if it is empty I should do something different.
I am stuck.
Thank you
You want to make use of the .exists() queryset option
#login_required(login_url='/login/')
def profile_page(request,username):
form = profile_form()
if Profile.objects.get(owner__username = username).exists():
query = Profile.objects.get(owner__username = username)
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},context_instance = RequestContext(request))
see QuerytSet API reference for more information
You just need to wrap that get query in try ... except and set instance to none, like this.
from django.core.exceptions import ObjectDoesNotExist
#login_required(login_url='/login/')
def profile_page(request,username):
try:
query = Profile.objects.get(owner__username = username)
#to be more specific you can except ProfileObjectDoesNotExist
except ObjectDoesNotExist:
query = None #Doesn't exist, set to None
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},
context_instance = RequestContext(request))
I think i may have use get_or_create for this purpose.
Profile.objects.get_or_create(owner__username = username)