Testing content of Django.contrib.messages for invalid forms - python

I'm trying to test the content of messages while processing ModelForms with Django. I've got the following View (assume there's a Thing model with a required name field):
#login_required
def update(request, thing_id):
thing = Thing.objects.get(id=thing_id) # assume this works
if request.method == "POST":
form = ThingModelForm(request.POST, instance=thing)
if form.is_valid():
form.save()
messages.success(request, "Success!")
return redirect("/wherever")
else:
messages.error(request, "Oops!")
else:
form = ThingModelForm(instance=thing)
args = ("myapp/update.html", {"form": form})
kwargs = {"context_instance": RequestContext(request)}
return render_to_response(*args, **kwargs)
Now, I've got two unit tests. The first tests valid data, while the second tests invalid data. (Note that client login happens during setUp):
def test_update_account(self):
url = reverse('update', args=[1]) # assume that's a valid id
resp = self.client.post(url, {"name": "foo"})
self.assertEqual(resp.status_code, 302)
m = resp.cookies.get('messages', '')
self.assertTrue("Success!" in m.output())
And now to test invalid data:
def test_update_account_failure(self):
url = reverse('update', args=[1]) # assume that's a valid id
resp = self.client.post(url, {"name": ""}) # name is required
self.assertEqual(resp.status_code, 200)
# This works:
self.assertTrue("Oops!" in resp.content)
# This fails:
m = resp.cookies.get('messages', '')
self.assertTrue("Oops!" in m.output())
Why would accessing the message's content through the cookie work in one instance but fail in another?

Two things you could check:
When you create the request self.client.post(url, {"name": ""}) is a Thing instance returned here: thing = Thing.objects.get(id=thing_id) If not it will not reach the line of code where you set your error message: messages.error(request, "Oops!") as Thing.objects.get will throw an error.
If there are no results that match the query, get() will raise a
DoesNotExist exception.
If the first thing does return a Thing instance, you could check whether a redirect return redirect("/wherever") after setting the error message messages.error(request, "Oops!") does change anything.

Related

how to loop through a python list of nothing

I am trying to create a online class and want to loop through the list of the classes to see if he/she been registered or not
problem is if the list be empty it will return an error
I am using django and django-restframework
here is my code
#api_view(['POST'])
#permission_classes([IsAuthenticated,])
def createOrderForOnlineClasses(request):
user = request.user
data = request.data
Class = OnlineClass.objects.get(id= data["classId"])
orderCred = {
'pin' : 'somepin',
'amount' : int(Class.totalPrice),
'callback' : 'http://localhost:3000/verify/',
}
for i in user.userprofile.onlineClass.all():
if i == Class:
return Response({"details": "allready registered"}, status=status.HTTP_400_BAD_REQUEST)
else:
try:
response = requests.post("URL_TO_SOMEWHERE", data=orderCred)
if response.status_code == 200 and not response.text.replace('-',"").isdigit():
registeredClass = RegisterStudentForOnlineClass.objects.create(
user=user,
totalPrice = int(Class.totalPrice),
transId = response.text,
onlineClassName= Class
)
serializer = RegisterForClassSerializer(registeredClass , many=False)
return Response(serializer.data)
else:
return Response({"details": ""} , status= status.HTTP_400_BAD_REQUEST)
except Exception as e:
return Response({"details": e})
here is the returned error
Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<class 'NoneType'>`
Thank you :)
when you call for i in user.userprofile.onlineClass.all() and it is empty it will simply pass the loop. Your problem is actually that you just need a default response for the scenario that user.userprofile.onlineClass.all() is empty.
Simply put a default expected response after the for loop

Rendering a "<class 'django.template.response.TemplateResponse'>" Object in Django

I have a requirement to incorporate my existing working openstack horizon with our SSO using python-saml.
Hence i referred to demo docs which is written here:
https://github.com/onelogin/python-saml/blob/master/demo-django/demo/views.py#L113
So here as per the guide, I need to render the page as mentioned.
return render(request, 'auth/login.html', {'errors': errors, 'not_auth_warn': not_auth_warn, 'success_slo': success_slo, 'paint_logout': paint_logout, 'SSO': True})
When I am trying the same I am not getting the expected result on page, to be exact, I am not getting the exact desired page with full details. here is the more detail on the same.
Expected page(This is screenshot of page working fine, before i am enabling saml):
Actual page now(Username and password text box not visible, Because login.html file is not enough to render it, because it need form to fully display, using django_auth_views response object "res" is created for the same):
Code:
def login(request, template_name=None, extra_context=None, **kwargs):
"""Logs a user in using the :class:`~openstack_auth.forms.Login` form."""
if not request.is_ajax():.
if (request.user.is_authenticated() and
auth.REDIRECT_FIELD_NAME not in request.GET and
auth.REDIRECT_FIELD_NAME not in request.POST):
return shortcuts.redirect(settings.LOGIN_REDIRECT_URL)
# Get our initial region for the form.
initial = {}
current_region = request.session.get('region_endpoint', None)
requested_region = request.GET.get('region', None)
regions = dict(getattr(settings, "AVAILABLE_REGIONS", []))
if requested_region in regions and requested_region != current_region:
initial.update({'region': requested_region})
if request.method == "POST":
if django.VERSION >= (1, 6):
form = functional.curry(forms.Login)
else:
form = functional.curry(forms.Login, request)
else:
form = functional.curry(forms.Login, initial=initial)
if extra_context is None:
extra_context = {'redirect_field_name': auth.REDIRECT_FIELD_NAME}
if not template_name:
if request.is_ajax():
template_name = 'auth/_login.html'
extra_context['hide'] = True
else:
template_name = 'auth/login.html'
res = django_auth_views.login(request,
template_name=template_name,
authentication_form=form,
extra_context=extra_context,
**kwargs)
# Save the region in the cookie, this is used as the default
# selected region next time the Login form loads.
if request.method == "POST":
utils.set_response_cookie(res, 'login_region',
request.POST.get('region', ''))
req = prepare_django_request(request)
auth = init_saml_auth(req)
errors = []
not_auth_warn = False
success_slo = False
attributes = False
paint_logout = False
if 'sso' in req['get_data']:
return HttpResponseRedirect(auth.login())
elif 'slo' in req['get_data']:
name_id = None
session_index = None
if 'samlNameId' in request.session:
name_id = request.session['samlNameId']
if 'samlSessionIndex' in request.session:
session_index = request.session['samlSessionIndex']
slo_built_url = auth.logout(name_id=name_id, session_index=session_index)
request.session['LogoutRequestID'] = auth.get_last_request_id()
print ('set logout id'+auth.get_last_request_id())
return HttpResponseRedirect(slo_built_url)
if 'samlUserdata' in request.session:
paint_logout = True
if len(request.session['samlUserdata']) > 0:
attributes = request.session['samlUserdata'].items()
return render(request, 'auth/login.html', {'errors': errors, 'not_auth_warn': not_auth_warn, 'success_slo': success_slo, 'paint_logout': paint_logout, 'SSO': True})
Would like to know what may be the problem here, What can be the fix to overcome this issue.
I have given try as below, tried returning the TemplateResponse object which was created in above mentioned code correct working code was just returning 'res' object from code.
return res
hence I tried to return the object instead of html file. ie: 'res' instead of 'auth/login.html'.
return render(request, res, {'errors': errors, 'not_auth_warn': not_auth_warn, 'success_slo': success_slo, 'paint_logout': paint_logout, 'SSO': True})
It returns error as follow:
Getting an error as follows:
ContentNotRenderedError at /auth/login/
The response content must be rendered before it can be accessed.
During analysis, I can see that template object(res), Which is of type: class 'django.template.response.TemplateResponse'
Someone please drop thoughts to figure out how we can resolve.

how to use context_processor properly in django

Here I am trying to redirect to another page if the form is submitted successfully but this code is not working properly .The code saves the form data sends the email , everything is fine but the problem is while redirecting to another page if the form succeed. The error I get is:
Django Version: 2.0.6
Exception Type: ValueError
Exception Value:
dictionary update sequence element #0 has length 0; 2 is required
context_processor.py
def volunteer_page2(request):
volunteer = Volunteer.objects.all().order_by('date')
if request.method == 'POST':
form = VForm(request.POST or None)
if form.is_valid():
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = "{0} with email address {1} has sent you new message \n\n{2}".format(name, email, form.cleaned_data['message'])
form.save(commit = False)
try:
send_mail(name, message, 'appname <settings.EMAIL_HOST_USER>', ['myemail'])
except:
return HttpResponse('Invalid header found')
form.save()
messages.success(request, 'Success')
return redirect('volunteer_page')
else:
messages.error(request, "Sorry try again")
else:
form = VForm()
return {'volunteer': volunteer, 'form':form}
views.py
def about_page(request):
about = About.objects.all().order_by('date')
banner = Banner.objects.all()
testimonial = Testimonial.objects.order_by('-pk')[0:2]
nav = Nav.objects.all()
footer = Footer.objects.all()
latest_event2 = Events.objects.order_by('-pk')[0:2]
context = {
'about': about,
'testimonial': testimonial,
'footer':footer,
'banner': banner,
'nav': nav,
'latest_event2': latest_event2,
}
return render(request, 'myapp/about.html', context)
settings.py
'myapp.context_processor.volunteer_page2'
Django's context processor should always return dictionary. In your code you are returning HttpResponse also. This is problem.

The view returned None (not returned an HttpResponse object)

This question might appear to be a duplicate and/or too boring. I have already read this, this, this, this and this questions & answers. But couldn't find solution that fits to my problem.
I'm new to Django framework. To learn it I want to create simple blog. When user clicks Register button (after filling required fields), following error thrown:
ValueError at /user/register/ The view user.views.register didn't
return an HttpResponse object. It returned None instead.
views.py
def register(request):
"""
Registers new user.
"""
if request.POST:
if request.method == 'POST':
personal_info = UserFormModel(request.POST)
if personal_info.is_valid():
email = personal_info.cleaned_data['email']
username = personal_info.cleaned_data['username']
if User.objects.filter(email=email).exists():
return HttpResponse('email error')
elif User.objects.filter(username=username).exists():
return HttpResponse('username error')
else:
return HttpResponse('saved')
else:
personal_info = UserFormModel()
return render_to_response('user/registration.html',
{
'title': 'Registration',
'username_error': 'Sorry, someone already has that username.',
'personal_info': personal_info,
},
context_instance=RequestContext(request))
If necessary I can share any files content.
Any helpful comment or answer would be appreciated.
In line
if personal_info.is_valid():
if personal info is not valid, it will return None.(return nothing)
add an else condition, because you are not handling a case.
if personal_info.is_valid():
# code here
else:
return HttpResponse('personal info provided not valid')
One of the better ways to handle situations like this from not occurring is to keep a dictionary for status message and result initialised at the start of the function, and return only at a single place instead of returning at multiple places, and storing result in result_dict.
result_dict = {'status': False}
and always return at the end of the function
return HttpResponse(json.dumps(result_dict))
This way you will never miss a case in returning values.
So Final code should look like
def register(request):
"""
Registers new user.
"""
result_dict = {'status': False}
if request.POST:
if request.method == 'POST':
personal_info = UserFormModel(request.POST)
if personal_info.is_valid():
email = personal_info.cleaned_data['email']
username = personal_info.cleaned_data['username']
if User.objects.filter(email=email).exists():
result_dict['message'] = 'email error'
elif User.objects.filter(username=username).exists():
result_dict['message'] = 'username error'
else:
result_dict['message'] = 'saved'
result_dict['status'] = True
else:
result_dict['message'] = 'personal info provided not valid'
else:
personal_info = UserFormModel()
return render_to_response('user/registration.html',
{
'title': 'Registration',
'username_error': 'Sorry, someone already has that username.',
'personal_info': personal_info,
},
context_instance=RequestContext(request))
return HttpResponse(json.dumps(result_dict))

Get Django Auth "User" id upon Form Submission

I currently have a model form that submits an entered domain to the db.
The problem I'm encountering is, I need to save the currently logged in user's ID (PK from the django.auth table) when a domain is submitted to satisfy a PK-FK relationship on the db end.
I currently have:
class SubmitDomain(ModelForm):
domainNm = forms.CharField(initial=u'Enter your domain', label='')
FKtoClient = User.<something>
class Meta:
model = Tld #Create form based off Model for Tld
fields = ['domainNm']
def clean_domainNm(self):
cleanedDomainName = self.cleaned_data.get('domainNm')
if Tld.objects.filter(domainNm=cleanedDomainName).exists():
errorMsg = u"Sorry that domain is not available."
raise ValidationError(errorMsg)
else:
return cleanedDomainName
and views.py
def AccountHome(request):
if request.user.is_anonymous():
return HttpResponseRedirect('/Login/')
form = SubmitDomain(request.POST or None) # A form bound to the POST data
if request.method == 'POST': # If the form has been submitted...
if form.is_valid(): # If form input passes initial validation...
domainNmCleaned = form.cleaned_data['domainNm'] ## clean data in dictionary
clientFKId = request.user.id
form.save() #save cleaned data to the db from dictionary`
try:
return HttpResponseRedirect('/Processscan/?domainNm=' + domainNmCleaned)
except:
raise ValidationError(('Invalid request'), code='300') ## [ TODO ]: add a custom error page here.
else:
form = SubmitDomain()
tld_set = request.user.tld_set.all()
return render(request, 'VA/account/accounthome.html', {
'tld_set':tld_set, 'form' : form
})
The problem is it gives me an error of: (1048, "Column 'FKtoClient_id' cannot be null"), very odd thing happening, for the column FKtoClient, its trying to submit: 7L instead of 7(the PK of this user's record). Any ideas?
If someone can please help, I would really appreciate it
Firstly, remove FKtoClient from your form. You need to set the user in your view where you can yes the request object. It's not possible to set an attribute on the form that automatically sets the current user.
When instantiating your form, you can pass a tld instance which already has the user set.
def AccountHome(request):
# I recommend using the login required decorator instead but this is ok
if request.user.is_anonymous():
return HttpResponseRedirect('/Login/')
# create a tld instance for the form, with the user set
tld = Tld(FKtoClient=request.user)
form = SubmitDomain(data=request.POST or None, instance=tld) # A form bound to the POST data, using the tld instance
if request.method == 'POST': # If the form has been submitted...
if form.is_valid(): # If form input passes initial validation...
domainNm = form.cleaned_data['domainNm']
form.save() #save cleaned data to the db from dictionary
# don't use a try..except block here, it shouldn't raise an exception
return HttpResponseRedirect('/Processscan/?domainNm=%s' % domainNm)
# No need to create another form here, because you are using the request.POST or None trick
# else:
# form = SubmitDomain()
tld_set = request.user.tld_set.all()
return render(request, 'VA/account/accounthome.html', {
'tld_set':tld_set, 'form' : form
})
This has an advantage over #dm03514's answer, which is that you can access the user within form methods as self.instance.user if required.
If you want to Require that a user be logged in to submit a form, you could do something like:
#login_required # if a user iS REQUIRED to be logged in to save a form
def your_view(request):
form = SubmitDomain(request.POST)
if form.is_valid():
new_submit = form.save(commit=False)
new_submit.your_user_field = request.user
new_submit.save()
You can get the logged in user from the request object:
current_user = request.user

Categories

Resources