Unit test fails saying 'WSGIRequest' object has no attribute 'city' - python

I have written a unit test to test an api....Its a GET call....
When I run it , i get this error ...Heres the traceback....
Traceback (most recent call last):
File "/home/arindam31/XXX-Name/mapi/tests.py", line 141, in test_get_cities
response = self.cl.get('/mapi/latest/cities/')
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 445, in get
response = super(Client, self).get(path, data=data, **extra)
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 229, in get
return self.request(**r)
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 387, in request
response = self.handler(environ)
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 84, in __call__
response = self.get_response(request)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 169, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 218, in handle_uncaught_exception
return callback(request, **param_dict)
File "/home/arindam31/XXX-Name/saul/views.py", line 546, in do_handle_500
return render_to_response("500.html", context_instance=RequestContext(request))
File "/usr/local/lib/python2.7/dist-packages/django/template/context.py", line 177, in __init__
self.update(processor(request))
File "/home/arindam31/XXX-Name/saul/context_processors.py", line 46, in common_context
ret_dict['city'] = request.city
AttributeError: 'WSGIRequest' object has no attribute 'city'
Heres my unit test...
def test_get_cities(self):
request = HttpRequest()
request.city = self.c1
response = self.cl.get('/mapi/latest/cities/')
content = response.content
data = json.loads(content)
for city in City.objects.all():
assert city.name in data
assert data[city.name] == city.pk
Here , self.c1 is a city type object in the setUp part....HttpRequest is from django.http.
The view being tested is below:
def get_cities(request):
print "1"
if ENABLE_HTTPS_CHECK and not request.is_secure():
return HttpResponseForbidden()
if request.method != 'GET':
return HttpResponseNotAllowed('Not allowed')
city_map = _get_city_map()
response = HttpResponse(json.dumps(city_map)
content_type='application/json')
response['Cache-Control'] = 'no-cache'
return response

If you want to test your view with your own request object, you should call the view directly. You should use the RequestFactory to create your request.
As Daniel Roseman points out your request object having a city attribute remains broken, unless you have some middleware that would set it. You clearly have some middleware that require it to be set in saul/context_processors.py.
Using RequestFactory and calling the view directly circumvents the middleware entirely, so you can concentrate on testing your view (if the view supports the absent middleware).
If your view requires the middelware to be operational, you probably just need to be logged in using the test client. It has a session pr test method.

I have no idea what you are trying to do here. You instantiate a request object, assign a city attribute, then proceed to ignore that object and just use the standard test client. I don't know why you would think that that request would be used for the client get.
To be honest though I think that your entire design is broken. You don't show how you expect to get the parameter into the request in a normal non-test scenario, but usually you would pass it via the POST or GET parameters rather than annotating it into the request somehow. That would of course make it easier to test, since you could just pass the dictionary to the client call.

Related

Access cleaned data from django admin for pdf processing

As a newbie, I am currently trying to add a print button to every existing django admin view of a project.
The goal is to make it not depend on a single model I could resolve manually for printing but every existing model which contains foreign keys to other models an so on.
For simplicity I thought it would be the best to just get the cleaned_data of the form and process it for the pdf.
I alread added the print button, the path url and so on and it will create a pdf file for me.
What I am not able to do is to access the forms cleaned_data from my BaseAdmins (extends the ModelAdmin) class like this:
form = BaseForm(request.POST)
if form.is_valid():
data = form.cleaned_data
It will just give me random kinds of errors, like object has no attribute 'is_bound'
So I think that I am generally wrong with the context where I am trying to get the cleaned_data from the form. Every tutorial I found is just showing how to get the data but not fully resolved if it contains foreign keys and not in which context.
Could you please clear up for me where it would make sense to pass any kind of form data maybe as session data or post body to a print view where I can process it.
Thank you very much for reading, hope I was able to describe my problem, feel free to ask.
Edit
This is the BaseForm I changed variable names for internal reasons:
class BaseForm(ModelForm):
def clean_custom(self):
another_model = self.cleaned_data.get('AnotherModel')
custom_models = self.cleaned_data.get('CustomModel')
custom_models_allowed = CustomModel.objects.filter(AnotherModel=another_model)
custom_models_was_list = True
if not custom_models:
return
if not isinstance(custom_models, Iterable):
custom_models_was_list = False
custom_models = [custom_models]
for custom_model in custom_models:
if another_model and custom_model not in custom_models_allowed:
custom_models_allowed = [custom_model.titel for custom_model in custom_models_allowed]
custom_models_allowed = ', '.join(custom_models_allowed)
raise ValidationError(
f'{custom_model} is not part of {another_model}. For selection: {custom_models_allowed}'
)
if custom_models_was_list:
return custom_models
else:
return custom_models[0]
I'm trying to add cleaned_data in my BaseAdmin class where I have access to request and get the following trace:
AttributeError
AttributeError: type object 'CustomForm' has no attribute 'is_bound'
Traceback (most recent call last)
File ".../.env/lib/python3.9/site-packages/django/contrib/staticfiles/handlers.py", line 65, in __call__
return self.application(environ, start_response)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/wsgi.py", line 141, in __call__
response = self.get_response(request)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/base.py", line 75, in get_response
response = self._middleware_chain(request)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File ".../.env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 125, in handle_uncaught_exception
return debug.technical_500_response(request, *exc_info)
File ".../.env/lib/python3.9/site-packages/django_extensions/management/technical_response.py", line 40, in null_technical_500_response
raise exc_value.with_traceback(tb)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File ".../.env/lib/python3.9/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File ".../.env/lib/python3.9/site-packages/django/contrib/admin/options.py", line 606, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File ".../.env/lib/python3.9/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File ".../.env/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File ".../.env/lib/python3.9/site-packages/django/contrib/admin/sites.py", line 223, in inner
return view(request, *args, **kwargs)
File ".../admin/base_admin.py", line 203, in change_view
if self.form.is_valid(self.form):
File ".../.env/lib/python3.9/site-packages/django/forms/forms.py", line 185, in is_valid
return self.is_bound and not self.errors
AttributeError: type object 'CustomForm' has no attribute 'is_bound'
This is not a standard question format, you are supposed to give us the code for the piece of code where the error occurs, (i.e. the base_admin.py file) but what you have provided is just 3 lines of it.
But so far I can see you are not checking whether the request is of the type of post or not. Pay attention to the if condition at the beginning of the method
def edit(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = PurposeForm(request.POST)
# check whether it's valid:
if form.is_valid():
for key, value in form.cleaned_data.items():
# etc

How should I handle exceptions raised in #jwt_required decorator? (in flask-jwt-extended)

I have a function with #jwt_required decorator.
class Test(Resource):
#jwt_required
def get(self):
return {"test": "ok" }
Which works fine when the correct HTTP header is set, i.e.
Authentication: Bearer [TOKEN]
but when the token is invalid/wrong or messed with, a jwt.exceptions.DecodeError is raised:
File "env/lib/python3.6/site-packages/flask_restplus/resource.py", line 44, in dispatch_request
resp = meth(*args, **kwargs)
File "env/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 103, in wrapper
verify_jwt_in_request()
File "env/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 32, in verify_jwt_in_request
jwt_data = _decode_jwt_from_request(request_type='access')
File "env/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 267, in _decode_jwt_from_request
decoded_token = decode_token(encoded_token, csrf_token)
File "env/lib/python3.6/site-packages/flask_jwt_extended/utils.py", line 80, in decode_token
encoded_token, verify=False, algorithms=config.algorithm
File "env/lib/python3.6/site-packages/jwt/api_jwt.py", line 84, in decode
payload, _, _, _ = self._load(jwt)
File "env/lib/python3.6/site-packages/jwt/api_jws.py", line 183, in _load
raise DecodeError('Not enough segments')
jwt.exceptions.DecodeError: Not enough segments
I cannot rely on clients always using correct tokens all the time.
And I cannot catch the exception because it is raised in the decorator rather than my own function. So the result is a http 500 error. How should I handle the exception more gracefully?
Flask-jwt-extended should be handling those for you gracefully. If not, you are probably using another extension (like flask-restful for example) that is breaking native flask functionality. You can try setting this option to fix it app.config[‘PROPAGATE_EXCEPTIONS’] = True, or take a look at this thread for some advice if you are using a different flask extension that is causing problems https://github.com/vimalloc/flask-jwt-extended/issues/86

How to send multiple files using rest_framework.test.APITestCase

I'm trying to send a couple of files to my backend:
class AccountsImporterTestCase(APITestCase):
def test(self):
data = [open('accounts/importer/accounts.csv'), open('accounts/importer/apartments.csv')]
response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
But I'm getting an error:
Error
Traceback (most recent call last):
File "/vagrant/conjuntos/accounts/tests/cases.py", line 128, in test
response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 168, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 89, in post
data, content_type = self._encode_data(data, format, content_type)
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 64, in _encode_data
ret = renderer.render(data)
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/renderers.py", line 757, in render
return encode_multipart(self.BOUNDARY, data)
File "/vagrant/venv/lib/python3.4/site-packages/django/test/client.py", line 156, in encode_multipart
for (key, value) in data.items():
AttributeError: 'list' object has no attribute 'items'
I know I'm not preparing the data correctly but is it possible to do it?, how?. Thanks!
Update: Trying #Kevin Brown solution
def test(self):
data = QueryDict('', mutable=True)
data.setlist('files', [open('accounts/importer/accounts.csv'), open('accounts/importer/apartments.csv')])
response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
Got the following:
Error
Traceback (most recent call last):
File "/vagrant/conjuntos/accounts/tests/cases.py", line 130, in test
response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 168, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 89, in post
data, content_type = self._encode_data(data, format, content_type)
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 64, in _encode_data
ret = renderer.render(data)
File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/renderers.py", line 757, in render
return encode_multipart(self.BOUNDARY, data)
File "/vagrant/venv/lib/python3.4/site-packages/django/test/client.py", line 182, in encode_multipart
return b'\r\n'.join(lines)
TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found
You are sending a list of files to the view, but you aren't sending them correctly. When you send data to a view, whether it is a Django view or DRF view, you are supposed to send it as a list of key-value pairs.
{
"key": "value",
"file": open("/path/to/file", "rb"),
}
To answer your question...
is it possible to do it?
It does not appear to be possible to upload multiple files using the same key (in tests), but it is possible to spread them out across multiple keys to achieve the same goal. Alternatively, you could set up your views to only handle a single file, and have multiple tests covering different test cases (apartments.csv, accounts.csv, etc.).
Your exception is being triggered because you are passing a single list instead of a dictionary, and Django cannot parse them correctly.
You might have some luck by directly forming the request dictionary using a QueryDict which is the internal representation of form data used by Django.
data = QueryDict(mutable=True)
data.setlist("files", [
open('accounts/importer/accounts.csv', 'rb'),
open('accounts/importer/apartments.csv', 'rb')
])
As this would more closely represent the data sent in through the browser. This has not been tested, but it's the way to send multiple non-file-values in one key.
Check if this solves your problem as from the errors its clearly says that the data should not be a list
data = {"account_csv": open('accounts/importer/accounts.csv'), "apartments_csv": open('accounts/importer/apartments.csv')}
This link you might find useful Uploading multiple files in a single request using python requests module

How to use AssertRaisesMessage() in Django tests

I followed the Django doc to write tests with assertRaisesMessage() but the problem is the exception itself is raised when executing test (so, the test is not executed).
Note that the exception called is an exception I voluntarily raise in a method of my model (not in the view).
class MyTestCase(TestCase):
def test_invertRewardState_view_fails_notmy_reward(self):
self.client.login(email='gislaine#toto.com', password='azerty')
resp = self.client.get(reverse(invertRewardState, args=(1,)))
self.assertRaisesMessage(
expected_exception=Exception,
expected_message=EXC_NOT_YOURS,
callable_obj=resp)
How Should I use AssertRaisesMessage() to let my test be executed without raising the Exception?
Thanks.
EDIT :
After trying falsetru 1st solution, the problem is still the same. As soon as my test enters in the resp = ... part, view is called, then related model method is called and raises the exception.
the full stack trace :
Traceback (most recent call last):
File "/Users/walt/Code/hellodjango/clientizr/tests.py", line 338, in test_invertRewardState_view_fails_notmy_reward
resp = self.client.get(reverse(invertRewardState, args=('1',)))
File "/Users/walt/Code/hellodjango/venv/lib/python2.7/site-packages/django/test/client.py", line 473, in get
response = super(Client, self).get(path, data=data, **extra)
File "/Users/walt/Code/hellodjango/venv/lib/python2.7/site-packages/django/test/client.py", line 280, in get
return self.request(**r)
File "/Users/walt/Code/hellodjango/venv/lib/python2.7/site-packages/django/test/client.py", line 444, in request
six.reraise(*exc_info)
File "/Users/walt/Code/hellodjango/venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/walt/Code/hellodjango/venv/lib/python2.7/site-packages/django/contrib/auth/decorators.py", line 22, in _wrapped_view
return view_func(request, *args, **kwargs)
File "/Users/walt/Code/hellodjango/clientizr/views.py", line 234, in invertRewardState
reward.invert_reward_state(request.user)
File "/Users/walt/Code/hellodjango/clientizr/models.py", line 606, in invert_reward_state
self.throw_error_if_not_owner_reward(cur_user)
File "/Users/walt/Code/hellodjango/clientizr/models.py", line 587, in throw_error_if_not_owner_reward
raise Exception(EXC_NOT_YOURS)
Exception: Cet objet n'est pas le v\xf4tre
You use assertRaisesMessage as a context manager around the code you expect to fail:
class MyTestCase(TestCase):
def test_invertRewardState_view_fails_notmy_reward(self):
self.client.login(email='gislaine#toto.com', password='azerty')
url = reverse(invertRewardState, args=(1,))
with self.assertRaisesMessage(Exception, EXC_NOT_YOURS):
self.client.get(url)
If you use self.client.get, you will not get an exception directly, but you can check status code.
def test_invertRewardState_view_fails_notmy_reward(self):
self.client.login(email='gislaine#toto.com', password='azerty')
resp = self.client.get(reverse(invertRewardState, args=('1',)))
self.assertEqual(resp.status_code, 500)
self.assertIn(EXC_NOT_YOURS in resp.content)
If you want to get an exception, call the view directly.
def test_invertRewardState_view_fails_notmy_reward(self):
request = HttpRequest()
request.user = User.objects.create(email='gislaine#toto.com') # login
self.assertRaisesMessage(Exception, EXC_NOT_YOURS, invertRewardState, '1')
You can use context manager form as Ned Batchelder suggested.

Why do I get "AttributeError: 'unicode' object has no attribute 'user' " on some specify url only?

I'm using the #login_required decorator in my project since day one and it's working fine, but for some reason, I'm starting to get "
AttributeError: 'unicode' object has no attribute 'user' " on some specific urls (and those worked in the past).
Example : I am the website, logged, and then I click on link and I'm getting this error that usually is linked to the fact that there is no SessionMiddleware installed. But in my case, there is one since I am logged on the site and the page I am on also had a #login_required.
Any idea?
The url is definied as : (r'^accept/(?P<token>[a-zA-Z0-9_-]+)?$', 'accept'),
and the method as : #login_required
def accept(request,token): ...
The Traceback:
Traceback (most recent call last):
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/core/servers/basehttp.py", line 674, in __call__
return self.application(environ, start_response)
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/core/handlers/wsgi.py", line 241, in __call__
response = self.get_response(request)
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/core/handlers/base.py", line 141, in get_response
return self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/core/handlers/base.py", line 165, in handle_uncaught_exception
return debug.technical_500_response(request, *exc_info)
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/core/handlers/base.py", line 100, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/contrib/auth/decorators.py", line 25, in _wrapped_view
return view_func(request, *args, **kwargs)
File "/Users/macbook/dev/pycharm-projects/proj/match/views.py", line 33, in accept
return __process(token,callback)
File "/Users/macbook/virtualenv/proj/lib/python2.6/site-packages/django/contrib/auth/decorators.py", line 24, in _wrapped_view
if test_func(request.user):
AttributeError: 'unicode' object has no attribute 'user'`
The decorator was on a private method that doesn't have the request as a parameter. I removed that decorator (left there because of a refactoring and lack of test [bad me]).
Problem solved.
This can also happen if you call a decorated method from another method without providing a request parameter.

Categories

Resources