django reusable redirect best practice - django

I've got many views that need the same functionality so I'm trying to move that logic into a separate function (not a view function). The function finds a value either in the GET or session and returns a model instance OR redirects to a new page (kind of like forcing a login). The problem is that you can't redirect from a called function (that I know of). How should I handle this scenario?
Here's my code:
# This is the called function
def getActiveShowOrRedirect(request):
show_pk = request.GET.get('s', False)
if not show_pk:
show_pk = request.session.get('show_pk', False)
if not show_pk:
return HttpResponseRedirect('/setup/')
active_show = Show.objects.get(pk=show_pk)
return active_show
def overview(request):
active_show = getActiveShowOrRedirect(request)
scenes = Scene.objects.filter(show=active_show)
scenes = sorted(scenes, key=lambda s: s.name)
if request.method == 'POST':
form = SceneForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
desc = form.cleaned_data['description']
scene = Scene(name=name.lower(), show=active_show, description=desc, creator=request.user)
scene.save()
return HttpResponseRedirect('/overview/')
else:
form = SceneForm(initial={'creator':request.user,'show':active_show})
return render_to_response('vfx_app/overview.html', {'active_show':active_show,'scenes':scenes,'form':form}, context_instance=RequestContext(request))
I suppose I could check the return type in the view function, but that seems kinda messy.

How about a decorator?
def requires_active_show(view):
"The decorated view must take active show as a second argument."
def wrapped(request, *args, **kw):
show_pk = request.GET.get('s') or request.session.get('show_pk')
if not show_pk:
return HttpResponseRedirect('/setup/')
return view(request, Show.objects.get(pk=show_pk), *args, **kw)
return wrapped
#requires_active_show
def overview(request, active_show):
scenes = Scene.objects.filter(show=active_show).order_by('name')
if request.method == 'POST':
form = SceneForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
desc = form.cleaned_data['description']
scene = Scene.objects.create(
name=name.lower(),
show=active_show,
description=desc,
creator=request.user)
return HttpResponseRedirect('/overview/')
else:
form = SceneForm(initial={'creator': request.user, 'show': active_show})
return render('vfx_app/overview.html', {
'active_show': active_show,
'scenes': scenes,
'form': form
})

Related

Form values are not overwritten when changed - they are stuck at their default values

I have the following view:
#login_required
def my_view(request):
instance = my_model(user=request.user)
form = my_model_form(request.POST,instance = instance)
if request.method == "POST":
if form.is_valid():
form.save(commit=False)
#Field1 and field2 is already in the form (its the input)
# Do some back-end operations to get values for the remaining fields
df = some_util_function()
form.field3 = df["field3"]
form.field4 = df["field4"]
form.field5= df["field5"]
form.save()
return redirect("my_html")
else:
form = my_model_form()
context = {
"form":form
}
return render(request, "discounttracker/my_html.html",context=context)
and the problem is that field3,field4,field5 are not changed. I have even tried to hard-code them to 1000, 2000,3000 (they are FloatField(default=0)) but they remain at their default value when written to the DB.
What am I doing wrong here?
You are setting the attributes of the form, not of the instance wrapped in the form. You should alter this to:
#login_required
def my_view(request):
instance = my_model(user=request.user)
if request.method == 'POST':
form = my_model_form(request.POST, instance = instance)
if form.is_valid():
# Field1 and field2 is already in the form (its the input)
# Do some back-end operations to get values for the remaining fields
df = some_util_function()
# ↓ the instance of the form
form.instance.field3 = df['field3']
form.instance.field4 = df['field4']
form.instance.field5 = df['field5']
form.save()
return redirect("my_html")
else:
form = my_model_form()
context = {
'form': form
}
return render(request, 'discounttracker/my_html.html', context=context)

the view didn't return an HttpResponse object. It returned None

For testing my form loggin, in the view index I return a dictionary. When I clicked on the submit button I receive this message error :
The view accueil.views.index didn't return an HttpResponse object. It returned None instead.
Where I made a mistake?
def index(request):
formConnex = ConnexionForm()
if request.method=='POST':
formConnex =ConnexionForm(request.POST)
if formConnex.is_valid():
envoi = True
surnom = formConnex.cleaned_data['surnom']
password = formConnex.cleaned_data['passeword']
formConnex = ConnexionForm()
dicInfoCon = {
'surnom_key':email,
'password_key':password,
'envoi_key':envoi
}
return render(request,'accueil/index.html',dicInfoCon)
else:
envoi = False
formConnex = ConnexionForm()
return render(request, 'accueil/index.html', 'formConnex_Key':formConnex})
The problem is simply that your final return line is indented too far. Move it back one indent, so that it also catches the case where request is POST but the form is not valid.
The problem is when the form is not valid. It does not return anything from function. To be exact:
if request.method=='POST':
formConnex =ConnexionForm(request.POST) # Please use snake_case
if formConnex.is_valid():
envoi = True
surnom = formConnex.cleaned_data['surnom']
password = formConnex.cleaned_data['passeword']
formConnex = ConnexionForm()
dicInfoCon = {
'surnom_key':email,
'password_key':password,
'envoi_key':envoi
}
return render(request,'accueil/index.html',dicInfoCon)
# Here should be an else block with how the view should handle if the form is not valid
So, you can update the method like this(I have added some refactoring):
def index(request):
formConnex = ConnexionForm(request.POST or None) # Please use snake_case, according to PEP-8 style guide
if request.method == 'POST':
if formConnex.is_valid():
envoi = True
surnom = formConnex.cleaned_data['surnom']
password = formConnex.cleaned_data['passeword']
dicInfoCon = {
'surnom_key': email,
'password_key': password,
'envoi_key': envoi
}
return render(request, 'accueil/index.html', dicInfoCon)
return render(request, 'accueil/index.html', {'formConnex_Key':formConnex})

django test ValueError: the view didn't return an HttpResponse object. It returned None instead

I have view to submit data. From the web browser, i can submit data and redirected nicely. But on the test, i got the error: didn't return an HttpResponse object. It returned None instead.
How can i pass my test?
The view code is:
def insert_barang_po(request, po_id=None):
"submit barang2 dalam PO, ditamilkan setelah insert PO"
po = get_object_or_404(PurchaseOrder, id=po_id)
context = {'menu_sales': True,}
context['po'] = {'id': po.id,}
if request.method == 'POST':
form = InsertBarangPOForm(request.POST)
if form.is_valid():
barang_po = BarangPurchaseOrder.objects.create(
po=form.cleaned_data['po'],
finish_good=form.cleaned_data['finish_good'],
jumlah_barang=form.cleaned_data['jumlah_barang'])
return redirect(reverse('sales:insert_barang_po', args=[po.id]))
else:
form = InsertBarangPOForm(initial={'po':po,})
context['form'] = form
return render(request, 'sales/insert_barang_po.html', context)
The model code is:
class BarangPurchaseOrder(models.Model):
"Menyimpan isi dari setiap PO yg diterima"
po = models.ForeignKey(PurchaseOrder)
finish_good = models.ForeignKey(FinishGood)
jumlah_barang = models.IntegerField()
def __unicode__(self):
return self.finish_good.nama
The test code is
def test_insert_barang_po_valid_po_id(self):
po = PurchaseOrder.objects.create(nomor_po='zaneti')
fg = FinishGood.objects.create(nama='selesai', code='sls')
c = Client()
response = c.post(reverse('sales:insert_barang_po', args=[po.id]),
{'po': po,
'finish_good': fg.id,
'jumlah_barang': '35',},
follow=True)
bpo = BarangPurchaseOrder.objects.filter(finish_good=fg) \
.order_by('id').reverse()
self.assertEqual(bpo[0].jumlah_barang, 35)
Your views.py doesn't cover all the cases. If the form is invalid, your method simply returns nothing, that's why you have this error message. You should have an else statement corresponding to if form.is_valid() statement.

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))