Django Rest: how to mock FileField and ImageField to_representation() - python

I have a model with 2 fields: image (ImageField) and media (FileField). They both use a custom GoogleCloudMediaStorage.
When writing my tests, I cannot access GCS, so I mocked the writing of the files by doing this:
from unittest.mock import patch
#patch('my_project.storage_backends.CustomGoogleCloudMediaStorage.save')
def test(self, mock_save):
mock_save.return_value = 'mocked_file.png'
# rest of the test
And it's working fine. The problem is now I have to test update and list views. When I do so, I have of course this error:
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application.
After digging it a bit, I see that the error is triggered by the serializer to_representation(). And more precisely in the FileField:
def to_representation(self, value):
if not value:
return None
use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
if use_url:
try:
url = value.url # !! THIS LINE CAUSE THE ISSUE
except AttributeError:
return None
request = self.context.get('request', None)
if request is not None:
return request.build_absolute_uri(url)
return url
So I tried to mock FileField.to_representation() doing this:
#patch('django.db.models.FileField.to_representation')
def test_update(self, mock_representation):
mock_representation.return_value = 'mocked_training_file_url'
data = {
'title': 'Updated',
}
response = self.client.patch(. . .)
But I have this error:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/unittest/mock.py", line 1322, in patched
with self.decoration_helper(patched,
File "/usr/local/lib/python3.8/contextlib.py", line 113, in __enter__
return next(self.gen)
File "/usr/local/lib/python3.8/unittest/mock.py", line 1304, in decoration_helper
arg = exit_stack.enter_context(patching)
File "/usr/local/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/usr/local/lib/python3.8/unittest/mock.py", line 1393, in __enter__
original, local = self.get_original()
File "/usr/local/lib/python3.8/unittest/mock.py", line 1366, in get_original
raise AttributeError(
AttributeError: <class 'django.db.models.fields.files.FileField'> does not have the attribute 'to_representation'
Any idea how I can mock FileField and ImageField to_representation() ? Am I doing this correctly

Related

Django Unit Testing: cannot mock django.core.urlresolvers.resolve

I'm trying to mock the django.core.urlresolvers.resolve function, but it doesn't seem to be work. I've tested with custom functions and it works like a charm, but mock ignores resolve completely.
Code:
test_something.py:
class SomethingTestCase(TestCase)
def setUp(self):
self.middleware = SomeMiddleware()
self.request = Mock()
self.request.session = {}
#patch('django.core.urlresolvers.resolve', side_effect=lambda: None)
def test_something(self):
self.assertEqual(self.middleware.process_request(self.request), None)
middleware.py
class SomeMiddleware(object):
def process_request(self, request):
app = resolve(request.path).app_name
print('App name: {}'.format(app))
This results in the following error:
======================================================================
ERROR: test_process_request_unauthenticated (something.tests.unit.test_middleware.SomethingTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/user/virtualenv/something/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "/home/user/projects/something/working/something/tests/unit/test_middleware.py", line 23, in test_process_request_unauthenticated
self.assertEqual(self.middleware.process_request(self.request), None)
File "/home/user/projects/something/working/something/middleware.py", line 14, in process_request
app = resolve(request.path).app_name
File "/home/user/virtualenv/something/local/lib/python2.7/site-packages/django/core/urlresolvers.py", line 534, in resolve
return get_resolver(urlconf).resolve(path)
File "/home/user/virtualenv/something/local/lib/python2.7/site-packages/django/core/urlresolvers.py", line 405, in resolve
raise Resolver404({'path': path})
Resolver404: {u'path': u"<Mock name='mock.path' id='140678271233168'>"}
My goal here is to make the resolve function returns something where I can get the app name.
Why is mock.patch unable to override the resolve function?
Firstly, you are patching it at the wrong location. You should patch it in file it is being used, and not where it is defined as more often than not the target code is imported before the patch is run.
Secondly, if the mocking works it would raise an error something on the lines of NoneType object has no attribute app_name.
#patch(<path_to_middleware.py>, side_effect=lambda: type('mock', (object,), {'app_name': 1})):
def test_something(self):
self.assertEqual(self.middleware.process_request(self.request), None)

Python - RuntimeError: working outside of request context

Trying to get the GET parameters from the URL. I have it working in my __init__.py file, but in a different file its not working.
I tried to use with app.app_context(): but I am still getting the same issue.
def log_entry(entity, type, entity_id, data, error):
with app.app_context():
zip_id = request.args.get('id')
RuntimeError: working outside of request context
Any suggestions?
Additional Info:
This is using Flask web framework which is setup as a service (API).
Example URL the user would hit http://website.com/api/endpoint?id=1
As mentioned above using `zip_id = request.args.get('id') works fine in the main file but I am in runners.py (just another file with definitions in)
Full traceback:
Debugging middleware caught exception in streamed response at a point where response headers were already sent.
Traceback (most recent call last):
File "/Users/ereeve/.virtualenvs/pi-automation-api/lib/python2.7/site-packages/werkzeug/wsgi.py", line 703, in __next__
return self._next()
File "/Users/ereeve/.virtualenvs/pi-automation-api/lib/python2.7/site-packages/werkzeug/wrappers.py", line 81, in _iter_encoded
for item in iterable:
File "/Users/ereeve/Documents/TechSol/pi-automation-api/automation_api/runners.py", line 341, in create_agencies
log_entry("test", "created", 1, "{'data':'hey'}", "")
File "/Users/ereeve/Documents/TechSol/pi-automation-api/automation_api/runners.py", line 315, in log_entry
zip_id = request.args.get('id')
File "/Users/ereeve/.virtualenvs/pi-automation-api/lib/python2.7/site-packages/werkzeug/local.py", line 343, in __getattr__
return getattr(self._get_current_object(), name)
File "/Users/ereeve/.virtualenvs/pi-automation-api/lib/python2.7/site-packages/werkzeug/local.py", line 302, in _get_current_object
return self.__local()
File "/Users/ereeve/.virtualenvs/pi-automation-api/lib/python2.7/site-packages/flask/globals.py", line 20, in _lookup_req_object
raise RuntimeError('working outside of request context')
RuntimeError: working outside of request context
Def in the same file calling the log_entry def
def create_agencies(country_code, DB, session):
document = DB.find_one({'rb_account_id': RB_COUNTRIES_new[country_code]['rb_account_id']})
t2 = new_t2(session)
log_entry("test", "created", 1, "{'data':'hey'}", "")

EncodeError: 'QuerySet' object has no attribute '_prefetch_related_lookups'

I recently updated my version of django from 1.2.5 to 1.7. Once done, all new transactions on my app were working as expected. However whenever I try to access a pickled object, I get the error
EncodeError: 'QuerySet' object has no attribute '_prefetch_related_lookups'
Here is the error thrown
'QuerySet' object has no attribute '_prefetch_related_lookups'
Traceback (most recent call last):
File "/foo/bar/gateway/baseGateway.py", line 108, in queueMessage
eng.processMessage(msgRow)
File "/foo/bar/engine/processor.py", line 101, in processMessage
tasks.deliverMessage.apply_async(args=[foo, bar], queue='message-deliver')
File "/opt/bitnami/python/lib/python2.7/site-packages/celery/app/task.py", line 555, in apply_async
**dict(self._get_exec_options(), **options)
File "/opt/bitnami/python/lib/python2.7/site-packages/celery/app/base.py", line 353, in send_task
reply_to=reply_to or self.oid, **options
File "/opt/bitnami/python/lib/python2.7/site-packages/celery/app/amqp.py", line 305, in publish_task
**kwargs
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/messaging.py", line 161, in publish
compression, headers)
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/messaging.py", line 237, in _prepare
body) = dumps(body, serializer=serializer)
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/serialization.py", line 164, in dumps
payload = encoder(data)
File "/opt/bitnami/python/lib/python2.7/contextlib.py", line 35, in __exit__
self.gen.throw(type, value, traceback)
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/serialization.py", line 59, in _reraise_errors
reraise(wrapper, wrapper(exc), sys.exc_info()[2])
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/serialization.py", line 55, in _reraise_errors
yield
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/serialization.py", line 164, in dumps
payload = encoder(data)
File "/opt/bitnami/python/lib/python2.7/site-packages/kombu/serialization.py", line 356, in pickle_dumps
return dumper(obj, protocol=pickle_protocol)
File "/opt/bitnami/python/lib/python2.7/site-packages/django/db/models/query.py", line 113, in __reduce__
return super(QuerySet, self).__reduce__()
File "/opt/bitnami/python/lib/python2.7/copy_reg.py", line 84, in _reduce_ex
dict = getstate()
File "/opt/bitnami/python/lib/python2.7/site-packages/django/db/models/query.py", line 91, in __getstate__
self._fetch_all()
File "/opt/bitnami/python/lib/python2.7/site-packages/django/db/models/query.py", line 967, in _fetch_all
if self._prefetch_related_lookups and not self._prefetch_done:
EncodeError: 'QuerySet' object has no attribute '_prefetch_related_lookups'
Looking at some of the solutions offered online and by django here and here, I cleared the sessions table in django to no avail.The error still persists. I use memcache in my application too and i cleared that. I also use celery.
Anyone know how to fix this?
I was seeing a related issue when trying to change a queryset on a model form within a view. The error was:
'NoneType' object has no attribute '_prefetch_related_lookups'
forms.py
class S1Form(forms.ModelForm):
library = forms.ModelChoiceField(
queryset = Library.objects.all(),
to_field_name = 'title',
required = True,
widget = forms.Select(
attrs = {
'class': 'custom-select'}
),
disabled = False,
empty_label = 'Select a library'
)
views.py
class FilteredSpectraSearchListView(SingleTableMixin, FilterView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['sform'] = S1Form()
context['sform'].fields['library'].queryset = None
if <something>:
context['sform'].fields['library'].queryset = <...>
elif <something-else>:
context['sform'].fields['library'].queryset = <...>
return context
The goal was to have an empty queryset initially which is later changed based on a few conditional statments. The problem was that the conditional "<something>" was not firing and None remained the queryset. The solution was simply to provide an empty queryset rather than None for this case:
...
context['sform'].fields['library'].queryset = Library.objects.none()
if <something>:
context['sform'].fields['library'].queryset = <...>
elif <something-else>:
context['sform'].fields['library'].queryset = <...>
...
Googling giving us a results
resetting sessions app fixed the issue (at least for the moment...)
https://code.djangoproject.com/ticket/18674
The problem is the Sessions, I had to delete them all for it to work. Addtionally I changed the settings SECRET_KEY so all sessions don’t validate.
http://jj.isgeek.net/2013/04/django-queryset-object-has-no-attribute-_prefetch_related_lookups/
You have some serialized data from Django < 1.4, and Kombu tries to deserialize it in your current Django version.
I don't know where Kombu saves its serialized data, but that's where you should look. You should either delete the stale data, or if you need to keep the data, manually change it to match your current Django version.

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

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.

Python/GAE Attribute Error - webapp2

I am using Nick Johnsons Webapp & Templates application as a base for what I am trying to do. I am getting a "AttributeError: 'NoneType' object has no attribute 'write'" when I try to call the render_template. I know it is because when I instantiate the object "Capture" as X, it doesn't have a response property. I have looked everywhere to find a solution but I cannot find one anywhere.
NOTE: There are other ways to do this, but I need it to work as closely to how I have it setup!
Traceback:
Traceback (most recent call last):
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 1535, in __call__
rv = self.handle_exception(request, response, e)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 1529, in __call__
rv = self.router.dispatch(request, response)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 1278, in default_dispatcher
return route.handler_adapter(request, response)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 1102, in __call__
return handler.dispatch()
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 572, in dispatch
return self.handle_exception(e, self.app.debug)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 570, in dispatch
return method(*args, **kwargs)
File "/Users/userx/Documents/_FRESHCUTZ MEDIA/Google/GAE - Test web form 1 /testwebform1/main.py", line 41, in post
x.calculateYear(name, age)
File "/Users/userx/Documents/_FRESHCUTZ MEDIA/Google/GAE - Test web form 1 /testwebform1/main.py", line 49, in calculateYear
self.response.write(self.render_template('index.html', **template_args))
AttributeError: 'NoneType' object has no attribute 'write'
MAIN.PY
import os
import webapp2
from webapp2_extras import jinja2
class BaseHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_template(self, filename, **template_args):
self.response.write(self.jinja2.render_template(filename, **template_args))
class MainPage(BaseHandler):
def get(self):
template_args = {}
self.render_template('index.html', **template_args)
class CaptureDetails(BaseHandler):
def post(self):
name = self.request.get("name").strip()
age = self.request.get("age").strip()
x = Calculate()
x.calculateYear(name, age)
class Calculate(BaseHandler):
def calculateYear(self, name, age):
template_args = {"age": age}
self.render_template('name.html', **template_args)
app = webapp2.WSGIApplication([
('/', MainPage),
('/capture', CaptureDetails),
('/age', Calculate)
], debug=True)
What am I doing wrong? Any help/suggestions are greatly appreciated!
Does it fit your criteria if you make calculateYear a function of your BaseHandler class (or elsewhere if more applicable)? As you guessed, your x isn't being treated as a proper response. When you invoke a webapp2.RequestHandler handler, it calls the method associated with the type of request (so in your case, since you are posting a form, it will call post(), as you know). When you instantiate x and call calculateYear, you aren't specifying a particular method (def get(self), def post(self), etc.), so there is no preparation of a response (when I have a chance I'll dig a little to confirm that this is actually the case - I could be mistaken :) ).
Can't test this right now, but assuming you need to call calculateYear from more than just the CaptureDetails handler, would this work? Here you would be referencing self in the context of your post method, which would invoke the response handling:
class BaseHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_template(self, filename, **template_args):
self.response.write(self.jinja2.render_template(filename, **template_args))
def calculateYear(self, name, age):
template_args = {"age": age}
self.render_template('name.html', **template_args)
Which you could then call from your CaptureDetails handler like:
class CaptureDetails(BaseHandler):
def post(self):
name = self.request.get("name").strip()
age = self.request.get("age").strip()
# Call the function and write the response
self.calculateYear(name, age)
By instantiating Calculate yourself in your CaptureDetails.post method you're not creating it in the same way that WSGIApplication does, so the properties aren't available. Specifically, you're not passing a response to it, so not surprisingly trying to reference it doesn't work.
In this case, I'd copy the contents of calculateYear into your post method - you're not really saving anything by having to create the instance and then call the method on it.
If calculateYear gets more complicated, and you don't want the duplication, then I'd introduce a new method that could be called by both of your hander methods. (It's not really clear from this example though why the Calculate class exists - it doesn't have a 'get' method, so mapping it to /age as you have done isn't going to work.)
Try using self.response.out.write instead of self.response.write.

Categories

Resources