Django test client get row id from query - django

How can i print the id from the response in the below code.The user does exist in the DB.Also i come across this error.
from django.test import Client
c = Client(enforce_csrf_checks=False)
response = c.post('/reg/_user/', {'firstname': 'test', 'lastname' : '_test'})
views get_user
def _user(request):
try:
response_dict = {}
qd = request.POST
firstname = qd.__getitem__('firstname')
lastname = qd.__getitem__('lastname')
up = UserProfile.objects.get(first_name=firstname,last_name=lastname)
print up.id
return up.id
except:
pass
Error:
response = c.post('/reg/_user/', {'firstname': 'test', 'lastname' : '_test'})
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 483, in post
response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 302, in post
return self.request(**r)
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 444, in request
six.reraise(*exc_info)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 201, in get_response
response = middleware_method(request, response)
File "/usr/local/lib/python2.7/dist-packages/django/middleware/clickjacking.py", line 30, in process_response
if response.get('X-Frame-Options', None) is not None:
AttributeError: 'UserProfile' object has no attribute 'get'

The problem is not with your tests, but with the view itself. In Django a view always has to return a HttpResponse object. Sometimes this is achieved using a shortcut function like render(), but it in turn also returns an HttpResponse object.
If for some reason you just want to return an otherwise empty page with this single value you could change
return up.id
to
return HttpResponse(up.id)
Also, I wonder: Did you create the view just to test UserProfile and don't use it as a view on the actual site? If so, this code doesn't belong in a view, it should be put into the unittest itself. You should only use the test client to test actual, real views.
On an mostly unrelated, but quite important note. This:
try:
# your view code
except:
pass
is a strong antipattern. Why would you want to silence all the potential problems? You should really stop doing that.

Related

Django REST framework. APITestCase. How test views which response for download data to csv

I have views like:
class StudentAPIPerformanceReport(
generics.RetrieveAPIView,
):
def get(self, request, *args, **kwargs):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="report.csv"'
writer = csv.writer(response)
for student in Student.objects.filter(pk=self.kwargs['pk']):
assigned_courses = CourseParticipant.objects.filter(student=student)
completed_courses = assigned_courses.filter(completed=True)
headings = (
"student full Name",
"number of assigned courses to student",
"number of completed courses by student"
)
rows = (
student.full_name,
assigned_courses.count(),
completed_courses.count()
)
writer.writerow(headings)
writer.writerow(rows)
return response
Urls:
path(
'student/report/<int:pk>/',
StudentAPIPerformanceReport.as_view(),
name='student_performance'
)
And test for it view:
class StudentAPIPerformanceReportTestCase(APITestCase):
def setUp(self):
self.student_obj = Student.objects.create(
first_name='test',
last_name='student',
email='test_student#gmail.com',
)
self.course_obj = Course.objects.create(
name='test',
)
student_obj = CourseParticipant.objects.create(
course_id=self.course_obj.pk,
student_id=self.student_obj.pk,
)
def test_student_unassigned_from_course(self):
data_id = self.student_obj.pk
rud_url = api_reverse('student:student_performance', kwargs={'pk': data_id})
get_response = self.client.get(rud_url, data_id)
self.assertEqual(get_response.status_code, status.HTTP_200_OK)
But i have Traceback:
Error
Traceback (most recent call last):
File "/home/project/test_task/student/tests.py", line 120, in test_student_unassigned_from_course
get_response = self.client.get(rud_url, data_id)
File "/home/project/test_task/venv/lib/python3.7/site-packages/rest_framework/test.py", line 292, in get
response = super(APIClient, self).get(path, data=data, **extra)
File "/home/project/test_task/venv/lib/python3.7/site-packages/rest_framework/test.py", line 199, in get
'QUERY_STRING': urlencode(data or {}, doseq=True),
File "/home/project/test_task/venv/lib/python3.7/site-packages/django/utils/http.py", line 93, in urlencode
for key, value in query:
TypeError: 'int' object is not iterable
Api which i wont to test just make some csv file in format:
test student,1,0
How i can test it? I will be grateful for the help
The error you are receiving is from the way you call self.client.get.
TypeError: 'int' object is not iterable
is about the second parameter that you are passing in this particular line:
get_response = self.client.get(rud_url, data_id)
# ______________________________________^
data_id itself is the student object's primary key, which is a simple integer id.
If you look closer at the Django docs here, the second parameter of get() is data, which is a dictionary, not an integer. That's why it tries to iterate this parameter, but it is not iterable.
Since you've already attached your primary key in the url (in the reverse()), there is no need to attach it as a query parameter of the GET request performed by self.client.get().
Once you remove data_id from the get() function call
get_response = self.client.get(rud_url)
it should work and you will successfully get the CSV file in the response, which you can test further.

TemplateView's get method: object has no attribute 'request'

There will be several tests: anonymous user from some pages will be redirected to login page. That's why some helper method was organized.
Now it is TemplateView, then it will be ListView etc.
When I try to transmit a request to the get method of TemplateView subclass, I get this error message: 'HomePageView' object has no attribute 'request'. But the signature of TemplateView's get method is def get(self, request, *args, **kwargs).
Could you give me a kick here?
/photoarchive/general/views.py
class HomePageView(TemplateView):
template_name = "general/home.html"
/photoarchive/general/tests.py
class GeneralTest(TestCase):
def test_anonymous_user_redirected_to_login_page(self, view_instance):
user = User(username='anonymous', email='vvv#mail.ru', password='ttrrttrr')
user.is_active = False
request = HttpRequest()
request.user = user
pdb.set_trace()
response = view_instance.get(request)
self.assertEqual(response.status_code, 302)
self.assertEqual(response['location'], '/accounts/login/')
def test_anonymous_user_from_home_page_redirected_to_login_page(self):
view_instance = HomePageView()
self.test_anonymous_user_redirected_to_login_page(view_instance)
Traceback:
ERROR: test_anonymous_user_from_home_page_redirected_to_login_page (general.tests.GeneralTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/michael/workspace/photoarchive/photoarchive/general/tests.py", line 49, in test_anonymous_user_from_home_page_redirected_to_login_page
self.test_anonymous_user_redirected_to_login_page(view_instance)
File "/home/michael/workspace/photoarchive/photoarchive/general/tests.py", line 23, in test_anonymous_user_redirected_to_login_page
response = view_instance.get(request)
File "/home/michael/workspace/venvs/photoarchive/lib/python3.5/site-packages/django/views/generic/base.py", line 158, in get
return self.render_to_response(context)
File "/home/michael/workspace/venvs/photoarchive/lib/python3.5/site-packages/django/views/generic/base.py", line 131, in render_to_response
request=self.request,
AttributeError: 'HomePageView' object has no attribute 'request'
----------------------------------------------------------------------
Ran 1 test in 0.028s
Interactive playing at pdb breakpoint:
-> response = view_instance.get(request)
(Pdb) view_instance
<general.views.HomePageView object at 0x7f9270d550f0>
(Pdb) request
<HttpRequest>
(Pdb) view_instance.get(request)
*** AttributeError: 'HomePageView' object has no attribute 'request'
(Pdb)
For reference:
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
A view that renders a template. This view will also pass into the context
any keyword arguments passed by the URLconf.
"""
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
Testing individual CBV methods is tricky. You aren't meant to instantiate the view with HomePageView().
In your urls.py, you do:
url(r'^$', HomePageView.as_view()),
Similarly, in your test, you can do HomePageView.as_view(). This returns the callable view which you can pass the request to.
view_instance = HomePageView.as_view()
response = view_instance(request)

Django Validation Error No Exception Supplied

I am making a post via jquery to my backend. I first look to see if a pledge has already been made. If not, I create one.
#csrf_exempt
def pledgeReceive(request):
fname = request.POST.get('fname', '')
lname = request.POST.get('lname', '')
participantName = request.POST.get('participantName', '')
participantID = request.POST.get('participantID', '')
ppl = request.POST.get('ppl', '')
maxi = request.POST.get('maxi', '')
sponsor = fname + ' ' + lname
participant_obj = Participant.objects.get(pk = participantID)
try:
pledge = Pledge.objects.get(sponsor = sponsor, participant = participant_obj, pledge_amount = ppl, max_pledge_amount = maxi, datetime = datetime.now())
except Pledge.DoesNotExist:
pledge = Pledge(sponsor = sponsor, participant = participant_obj, pledge_amount = ppl, max_pledge_amount = maxi, datetime = datetime.now())
pledge.save()
response = HttpResponse()
response.content = serialized_obj = serializers.serialize('json', [ pledge, ])
response['Content-Type'] = 'application/json'
return response
I get a 500 internal server error. With the following traceback:
ValidationError at /pledgeReceive/
No exception supplied
Traceback:
File "/home/vtrelayc/lib/python2.6/site-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/home/vtrelayc/lib/python2.6/site-packages/django/views/decorators/csrf.py" in wrapped_view
77. return view_func(*args, **kwargs)
File "/home/vtrelayc/projects/relay/relayapp/views.py" in pledgeReceive
461. pledge.save()
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/base.py" in save
463. self.save_base(using=using, force_insert=force_insert, force_update=force_update)
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/base.py" in save_base
551. result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw)
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/manager.py" in _insert
203. return insert_query(self.model, objs, fields, **kwargs)
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/query.py" in insert_query
1593. return query.get_compiler(using=using).execute_sql(return_id)
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/sql/compiler.py" in execute_sql
911. for sql, params in self.as_sql():
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/sql/compiler.py" in as_sql
872. for obj in self.query.objs
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/fields/__init__.py" in get_db_prep_save
873. return connection.ops.value_to_db_decimal(self.to_python(value),
File "/home/vtrelayc/lib/python2.6/site-packages/django/db/models/fields/__init__.py" in to_python
850. raise exceptions.ValidationError(msg)
Exception Type: ValidationError at /pledgeReceive/
I am providing an exception though with except Pledge.DoesNotExist:?
Why not just use get_or_create? It is a shortcut for what you are attempting to do.
pledge, created = Pledge.objects.get_or_create(sponsor = sponsor, participant = participant_obj, pledge_amount = ppl, max_pledge_amount = maxi, datetime = datetime.now())
Now you have a pledge object and a boolean, created, that lets you know whether the object was created or fetched.
Other than that, the error is probably caused by the fact that you are just getting the GET parameters without casting them to the types that your Pledge object expects. They are getting passed as strings even though most of your fields probably aren't expecting strings. Maybe use a form (for it's built in validation/type casting) or manually convert your GET parameters to the correct types.
ValidationError exception is raised when data fails form or model field validation but because your not using form.is_valid() or any validator which validate your input values this will result to No Exception Supplied. Even your Pledge.DoesNotExist cannot catch the error because it's only check when an object is not found for the given parameters of a query.
Based on your code, I found invalid values.
ppl = request.POST.get('ppl', '')
maxi = request.POST.get('maxi', '')
If I'm not wrong the code above pass an integer. But you didn't know your passing a string not an int. It must be,
ppl = int(request.POST.get('ppl', 0))
maxi = int(request.POST.get('maxi', 0))
I don't know your model code pattern, if your passing a value for foreignkey or date your must also check that because they are very sensitive. You don't have to worry about CharField because they can handle it unless if required. But I suggest you use form, if you can't handle this error.

Sending JSON using the django test client

I'm working on a django a project that will serve as the endpoint for a webhook. The webhook will POST some JSON data to my endpoint, which will then parse that data. I'm trying to write unit tests for it, but I'm not sure if I'm sending the JSON properly.
I keep getting "TypeError: string indices must be integers" in pipeline_endpoint
Here's the code:
# tests.py
from django.test import TestCase
from django.test.client import Client
import simplejson
class TestPipeline(TestCase):
def setUp(self):
"""initialize the Django test client"""
self.c = Client()
def test_200(self):
json_string = u'{"1": {"guid": "8a40135230f21bdb0130f21c255c0007", "portalId": 999, "email": "fake#email"}}'
json_data = simplejson.loads(json_string)
self.response = self.c.post('/pipeline-endpoint', json_data, content_type="application/json")
self.assertEqual(self.response.status_code, "200")
and
# views.py
from pipeline.prospect import Prospect
import simplejson
def pipeline_endpoint(request):
#get the data from the json object that came in
prospects_json = simplejson.loads(request.raw_post_data)
for p in prospects_json:
prospect = {
'email' : p['email'],
'hs_id' : p['guid'],
'portal' : p['portalId'],
}
Edit: whole traceback.
======================================================================
ERROR: test_200 (pipeline.tests.TestPipeline)
----------------------------------------------------------------------
Traceback (most recent call last):
File "F:\......\pipeline\tests.py", line 31, in test_200
self.response = self.c.post('/pipeline-endpoint', json_string, content_type="application/json")
File "C:\Python27\lib\site-packages\django\test\client.py", line 455, in post
response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
File "C:\Python27\lib\site-packages\django\test\client.py", line 256, in post
return self.request(**r)
File "C:\Python27\lib\site-packages\django\core\handlers\base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "F:\......\pipeline\views.py", line 18, in pipeline_endpoint
'email' : p['email'],
TypeError: string indices must be integers
----------------------------------------------------------------------
Ran 1 test in 0.095s
FAILED (errors=1)
Destroying test database for alias 'default'...
#mrmagooey is right
def test_your_test(self):
python_dict = {
"1": {
"guid": "8a40135230f21bdb0130f21c255c0007",
"portalId": 999,
"email": "fake#email"
}
}
response = self.client.post('/pipeline-endpoint/',
json.dumps(python_dict),
content_type="application/json")
use json.dumps instead of json.loads
Try:
self.client.generic('POST', '/url', json.dumps({'json': 'object'})
rest_framework's APIClient (which is the the default client_class in APITestCase) takes care of dumping dict to JSON and it sets proper content type by passing format='json'.
from rest_framework import status
from rest_framework.test import APIClient, APITestCase
class MyTestCase(APITestCase):
url = '/url'
def post(self, payload, url=None):
"""
Helper to send an HTTP post.
#param (dict) payload: request body
#returns: response
"""
if url is None:
url = self.url
return self.client.post(url, payload, format='json')
def test_my_function(self):
payload = {
'key': 'value'
}
response = self.post(payload)
self.assertEqual(response.status_code, status.HTTP_200_OK)
You can always use the HttpRequest.body which loads the raw request data. This way you can handle your own data processing.
c = Client()
json_str= json.dumps({"data": {"id": 1}})
c.post('/ajax/handler/', data= json_str, content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
def index(request):
....
print json.loads(request.body)
Since Django 3.0:
If you provide content_type as application/json, the data is
serialized using json.dumps() if it’s a dict, list, or tuple.
Serialization is performed with DjangoJSONEncoder by default, and can
be overridden by providing a json_encoder argument to Client. This
serialization also happens for put(), patch(), and delete() requests.
response = client.post(
f'/customer/{customer.id}/edit',
{'email': new_email},
content_type='application/json'
)
You can user iteritems on dictionaries to loop
for index, p in prospects_json.iteritems():
prospect={
'email': p['email'],
}
or alternatively
for index in prospect_json:
prospect={
'email': prospect_json[ index ]['email']
}
Adding to Guillaume Vincent's answer, from Django 2.1 we no longer need to use json.dumps for passing the data.
Changed in Django 2.1:
The JSON serialization described above was added. In older versions, you can call json.dumps() on data before passing it to post() to achieve the same thing.

Django soaplib error

I'm trying to make a little "Hello World" webservice with Django following a few tutorials, but I'm hitting the same barrier over and over. I've defined a view.py and soaplib_handler.py:
view.py:
from soaplib_handler import DjangoSoapApp, soapmethod, soap_types
class HelloWorldService(DjangoSoapApp):
__tns__ = 'http://saers.dk/soap/'
#soapmethod(_returns=soap_types.Array(soap_types.String))
def hello(self):
return "Hello World"
soaplib_handler.py:
from soaplib.wsgi_soap import SimpleWSGISoapApp
from soaplib.service import soapmethod
from soaplib.serializers import primitive as soap_types
from django.http import HttpResponse
class DjangoSoapApp(SimpleWSGISoapApp):
def __call__(self, request):
django_response = HttpResponse()
def start_response(status, headers):
status, reason = status.split(' ', 1)
django_response.status_code = int(status)
for header, value in headers:
django_response[header] = value
response = super(SimpleWSGISoapApp, self).__call__(request.META, start_response)
django_response.content = "\n".join(response)
return django_response
And it seems the "response = super...." line is giving me trouble. When I load up /hello_world/services.wsdl mapped in url.py I get:
AttributeError at /hello_world/service.wsdl
'module' object has no attribute 'tostring'
For the full error message, see here:
http://saers.dk:8000/hello_world/service.wsdl
Do you have any suggestion as to why I get this error? And where is ElementTree defined?
#zdmytriv The line
soap_app_response = super(BaseSOAPWebService, self).__call__(environ, start_response)
should look like
soap_app_response = super(DjangoSoapApp, self).__call__(environ, start_response)
then your example works.
not sure if this will solve your problem, but the decorator on your function hello says that it is suppose to return a String Array, but you are actually returning a String
Try _returns=soap_types.String instead
Ray
Copy/paste from my service:
# SoapLib Django workaround: http://www.djangosnippets.org/snippets/979/
class DumbStringIO(StringIO):
""" Helper class for BaseWebService """
def read(self, n):
return self.getvalue()
class DjangoSoapApp(SimpleWSGISoapApp):
def __call__(self, request):
""" Makes Django request suitable for SOAPlib SimpleWSGISoapApp class """
http_response = HttpResponse()
def start_response(status, headers):
status, reason = status.split(' ', 1)
http_response.status_code = int(status)
for header, value in headers:
http_response[header] = value
environ = request.META.copy()
body = ''.join(['%s=%s' % v for v in request.POST.items()])
environ['CONTENT_LENGTH'] = len(body)
environ['wsgi.input'] = DumbStringIO(body)
environ['wsgi.multithread'] = False
soap_app_response = super(BaseSOAPWebService, self).__call__(environ, start_response)
http_response.content = "\n".join(soap_app_response)
return http_response
Django snippet has a bug. Read last two comments from that url.