How to cache a view method without a route? - django

I'm trying to test caching in my code. I am using memcached as the backend. I set the CACHE config to use memcached under 'basic'. There isn't a direct route to the get_stuff method. Here is my code:
I have a view that looks like
from .models import MyModel
from django.views.decorators.cache import cache_page
class MyView(TemplateView):
""" Django view ... """
template_name = "home.html"
#cache_page(60 * 15, cache="basic")
def get_stuff(self): # pylint: disable=no-self-use
""" Get all ... """
return MyModel.objects.filter(visible=True, type=MyModel.CONSTANT)
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
stuffs = self.get_stuff()
if stuffs:
context['stuff'] = random.choice(stuffs)
return context
I also have a test that looks like
from django.test.client import RequestFactory
from xyz.apps.appname import views
class MyViewTestCase(TestCase):
""" Unit tests for the MyView class """
def test_caching_get_stuff(self):
""" Tests that we are properly caching the query to get all stuffs """
view = views.MyView.as_view()
factory = RequestFactory()
request = factory.get('/')
response = view(request)
print response.context_data['stuff']
When I run my test I get this error:
Traceback (most recent call last):
File "/path/to/app/appname/tests.py", line 142, in test_caching_get_stuff
response = view(request)
File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py", line 69, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py", line 87, in dispatch
return handler(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py", line 154, in get
context = self.get_context_data(**kwargs)
File "/path/to/app/appname/views.py", line 50, in get_context_data
stuffs = self.get_stuff()
File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py", line 91, in _wrapped_view
result = middleware.process_request(request)
File "/usr/local/lib/python2.7/site-packages/django/middleware/cache.py", line 134, in process_request
if not request.method in ('GET', 'HEAD'):
AttributeError: 'MyView' object has no attribute 'method'
What is causing this and how do I fix this? I'm fairly new to Python and Django.

Can you show the what you have for MIDDLEWARE_CLASSES in settings.py? I looked through the code where your error showed up, and it notes that FetchFromCacheMiddleware must be last piece of middleware in the MIDDLEWARE_CLASSES. I wonder if that is causing your problem.
Related documentation here.

I believe the issue is that the cache_page decorator is meant to be used on view functions, not to decorate methods of class-based views. View functions take 'request' as their first argument, and if you look at the traceback, you can see that in fact the error is caused because the first argument of the function you tried to decorate is not a request but rather 'self' (i.e., the MyView object referred to in the error).
I am not sure if or how the cache_page decorator can be used for class-based views, though this page in the docs suggests a way of using it in the URLconf and I imagine you could wrap the return of ViewClass.as_view in a similar fashion. If the thing you're trying to wrap in caching is not properly a view but rather a utility function of some sort, you should drop to using the more manual lower-level cache API inside of your function (not as a decorator).

Related

Django Rest Framework URL for custom action not working

I've got the following custom action in my view:
class OrderAPIViewSet(viewsets.ViewSet):
def create(self, request):
print("Here: working")
#action(detail=True, methods=['post'])
def add(self, request, *arg, **kwargs):
print("HERE in custom action")
order = self.get_object()
print(order)
my app's urls.py is:
from rest_framework import routers
from .views import OrderAPIViewSet
router = routers.DefaultRouter()
router.register(r'orders', OrderAPIViewSet, basename='order')
urlpatterns = router.urls
So in my test when I try to access orders/post it works, but when I try to access orders/{pk}/add it fails. I mean, the reverse itself is failing:
ORDERS_LIST_URL = reverse('order-list')
ORDERS_ADD_URL = reverse('order-add')
class PublicOrderApiTests(TestCase):
def test_sample_test(self):
data = {}
res = self.client.post(ORDERS_ADD_URL, data, format='json')
as I said before, I've got a separate test where I use ORDERS_LIST_URL like this:
res = self.client.post(ORDERS_LIST_URL, data, format='json')
but when running the test I'm getting the following error:
ImportError: Failed to import test module: orders.tests Traceback
(most recent call last): File
"/usr/local/lib/python3.7/unittest/loader.py", line 436, in
_find_test_path
module = self._get_module_from_name(name) File "/usr/local/lib/python3.7/unittest/loader.py", line 377, in
_get_module_from_name
import(name) File "/app/orders/tests.py", line 22, in
ORDERS_ADD_URL = reverse('order-add') File "/usr/local/lib/python3.7/site-packages/django/urls/base.py", line 87,
in reverse
return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)) File "/usr/local/lib/python3.7/site-packages/django/urls/resolvers.py",
line 685, in _reverse_with_prefix
raise NoReverseMatch(msg) django.urls.exceptions.NoReverseMatch: Reverse for 'order-add' with no arguments not found. 2 pattern(s)
tried: ['orders/(?P[^/.]+)/add\.(?P[a-z0-9]+)/?$',
'orders/(?P[^/.]+)/add/$']
---------------------------------------------------------------------- Ran 1 test in 0.000s
FAILED (errors=1)
according to the documentation I shouldn't need to register this endpoint, the router is supposed to do it by itself. What am I missing?
The first thing that you've missed is pk in your reverse. Since the add API needs a pk of your Order object, you need to pass it to reverse function. For example:
order_add_url = reverse('order-add', kwargs={'pk': 1})
print(order_add_url) # which will print '/orders/1/add/'
So I think you should move this part to the body of PublicOrderApiTests's methods since you need a dynamic url per test object.
Another problem is that the ViewSet class does not support self.get_object() and if you want to use this method you should either have your own method or use rest framework GenericViewSet (i.e. from rest_framework.viewsets import GenericViewSet and inherit from this class instead of ViewSet) then you can access the get_object() method. You can also read more about generic views in rest framework docs.

Exporting data from App Engine datastore as a Google Drive spreadsheet

I have an app engine application on which I mark my monthly expenses along with some comments or reason. I would like to export these data into a
Google Drive Spreadsheet. I use Django framework.
I had gone through the tutorials provided by Google here.
But they have implemented it using webapp2 and jinja. Moreover, the doc for Implementing Using Django seems way too obsolete since I do not use Django ORM.
Below is my code sample which I use to upload. I strongly apologize if what I paste below is rubbish. Please help.
from django.utils.datastructures import SortedDict
import os
from apiclient.discovery import build
from apiclient.http import MediaFileUpload
from oauth2client.appengine import OAuth2DecoratorFromClientSecrets
decorator = OAuth2DecoratorFromClientSecrets(os.path.join(os.path.dirname(__file__ ), 'clientSecrets.json'), 'https://www.googleapis.com/auth/drive')
drive_service = build('drive', 'v2')
class Exporter(object):
serializedObjects = []
mime_type = 'text/plain'
fileToExport = None
request = None
def __init__(self, serializedObjects, request):
self.serializedObjects = serializedObjects
self.request = request
def createCSV(self):
import csv
import StringIO
stdout = StringIO.StringIO()
writer = csv.writer(stdout)
for obj in self.serializedObjects:
for value in obj.values():
writer.writerow([value])
# I will get the csv produced from my datastore objects here.
# I would like to upload this into a Google Spreadsheet.
# The child class ExportToSpreadSheet tries to do this.
return stdout.getvalue()
class ExportToSpreadSheet(Exporter):
def __init__(self, *args, **kwargs):
super(ExportToSpreadSheet, self).__init__(*args, **kwargs)
self.mime_type = 'application/vnd.google-apps.spreadsheet'
def create(self):
import datetime
valueToDrive = self.createCSV()
media_body = MediaFileUpload(valueToDrive, mimetype=self.mime_type, resumable=True)
body = {
'title' : 'MyExpense_%s' % datetime.datetime.now().strftime('%d_%b_%Y_%H_%M_%S'),
'description' : '',
'mimeType' : self.mime_type
}
self.fileToExport = drive_service.files().insert(body=body, media_body=media_body, convert=True)
return self.fileToExport
#decorator.oauth_aware
def upload(self):
if decorator.has_credentials():
self.create()
self.fileToExport.execute(decorator.http())
return self.fileToExport
raise Exception('user does not have the credentials to upload to google drive.')
#decorator.oauth_aware only works on webapp.RequestHandler subclasses. Why I am saying it is because I got this error while I ran the code.
INFO 2013-09-19 11:28:04,550 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/drive/v2/rest?userIp=%3A%3A1
ERROR 2013-09-19 11:28:05,670 main.py:13] Exception in request:
Traceback (most recent call last):
File "/home/dev5/divya/jk/MyApp/ItsMyTuition/SDK/google_appengine/lib/django-1.2/django/core/handlers/base.py", line 100, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/dev5/divya/jk/MyApp/ItsMyTuition/ItsMyTuition/src/tuition/json/ajaxHandler.py", line 27, in mainHandler
responseValues = funtionToCall(*args)
File "/home/dev5/divya/jk/MyApp/ItsMyTuition/ItsMyTuition/src/tuition/json/ajaxHandler.py", line 69, in export
uploadedFile = exporterInstance.upload()
File "/home/dev5/divya/jk/MyApp/ItsMyTuition/ItsMyTuition/src/oauth2client/appengine.py", line 770, in setup_oauth
self._create_flow(request_handler)
File "/home/dev5/divya/jk/MyApp/ItsMyTuition/ItsMyTuition/src/oauth2client/appengine.py", line 734, in _create_flow
redirect_uri = request_handler.request.relative_url(
AttributeError: 'ExportToSpreadSheet' object has no attribute 'request'
INFO 2013-09-19 11:28:05,777 module.py:593] default: "POST /ajaxCall/export HTTP/1.1" 200 964
Since I am using Django framework I cannot get a Request handler as they except.
How can I integrate or do it in my scenario? I would very much appreciate any code samples or relevant links I may have missed.
Moreover, the whole thing happens in an ajax call.
Thanks in advance.
Use mimeType=text/csv and during the upload, request a conversion from csv to Spreadsheets:
drive_service.files().insert(covert=True, body=body, media_body=media_body, convert=True)

testing django web app that uses cookies/session

In views.py:
get_dict = Site.objects.getDictionary(request.COOKIES['siteid'])
{gets a dictionary with site information based on id from cookie}
In tests.py:
from django.test import TestCase
class WebAppTest(TestCase):
def test_status(self):
response = self.client.get('/main/',{})
response.status_code # --->passed with code 200
response = self.client.get('/webpage/',{'blog':1})
response.status_code # ----> this is failing
In order to present blog page it goes to a view where it gets a dictionary using existing cookie, process it, renders templates, which works fine when running the app. But the tests are failing.Having never tested Django webapps I'm not sure how to test it right. Here is the traceback.
Traceback (most recent call last):
File "<console>", line 2, in <module>
File "/usr/lib/pymodules/python2.6/django/test/client.py", line 313, in post
response = self.request(**r)
File "/usr/lib/pymodules/python2.6/django/core/handlers/base.py", line 92, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/var/lib/django/data/../webpage/views.py", line 237, in getCostInfo
get_dict = Site.objects.getDictionary(request.COOKIES['siteid'])
KeyError: 'siteid'
Went through some online samples but couldn't find something that deals in depth with cookies/sessions. Any ideas or directs to useful links are highly appreciated.
Take a look at the Persistent State section of the Django Testing docs.
In your case, I would expect your test to be something more like:
from django.test import TestCase
from django.test.client import Client
class WebAppTest(TestCase):
def setUp(self):
self.client = Client()
session = self.client.session
session['siteid'] = 69 ## Or any valid siteid.
session.save()
def test_status(self):
response = self.client.get('/main/',{})
self.assertEqual(response.status_code, 200)
response = self.client.get('/webpage/',{'blog':1})
self.assertEqual(response.status_code, 200)

Monkey patched django auth's login, now its tests fail

My app seeks to wrap the django.contrib.auth.views login and logout views with some basic auditing/logging capabilities. I'm following the prescription as described in the django-axes project, and in running on a server and some other tests, it works as expected, transparently without issue.
The code goes like this:
from django.contrib.auth import views as auth_views
from myapp.watchers import watch_login
class WatcherMiddleware(object):
def __init__(self):
auth_views.login = watch_login(auth_views.login)
And
def watch_login(func):
def decorated_login(request, *args, **kwargs):
#do some stuff
response = func(request, *args, **kwargs)
#more stuff
return response
return decorated_login
Urls:
#Edit: Added project's urls - just using vanilla django's auth login
(r'^accounts/login/$', 'django.contrib.auth.views.login',{"template_name":settings.LOGIN_TEMPLATE }),
However, in our build workflow, we run into some issues in the django.contrib.auth.tests.views.
Specifically, these are the tests that fail in django.contrib.auth:
ERROR: test_current_site_in_context_after_login (django.contrib.auth.tests.views.LoginTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python26\lib\site-packages\django\contrib\auth\tests\views.py", line 192, in test_current_site_in_context_after_login
response = self.client.get(reverse('django.contrib.auth.views.login'))
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 351, in reverse
*args, **kwargs)))
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 297, in reverse
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
NoReverseMatch: Reverse for 'myapp.watchers.decorated_login' with arguments '()' and keyword arguments '{}' not found.
======================================================================
ERROR: test_security_check (django.contrib.auth.tests.views.LoginTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python26\lib\site-packages\django\contrib\auth\tests\views.py", line 204, in test_security_check
login_url = reverse('django.contrib.auth.views.login')
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 351, in reverse
*args, **kwargs)))
File "C:\Python26\lib\site-packages\django\core\urlresolvers.py", line 297, in reverse
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
NoReverseMatch: Reverse for 'myapp.watchers.decorated_login' with arguments '()' and keyword arguments '{}' not found.
Only these two tests fail with the inclusion of the wrapped login monkey patch.
It seems like the reverse() call in the django auth test behaves differently than how an unpatched function does its thing.
The reason why we're going this route for wrapping logging vs. using django 1.3's new authentication signals is because the logging method provided there only tells you if a wrong attempts happens - it doesn't give you access to the request object to log additional information around that improper request. Patching the authentication form in that case would not have been helpful, hence our need to wrap the login function.
Am I doing something wrong with my wrap of the login function? Or is this as to be expected with tests failing due to other side effects, despite no change in overall functionality?
edit: I'm running python 2.6.4, django 1.2.5
Couldn't you simply wrap it in another view?
urls:
url(
r'^accounts/login/$',
'accounts.views.login',
{"template_name":settings.LOGIN_TEMPLATE }
),
accounts.views:
from django.contrib.auth import views as auth_views
def login(request, *args, **kwars):
# do some stuff
response = auth_views.login(request, *args, **kwars)
# more stuff
return response
Like this, django.contrib.auth.tests will be testing the view which they were written for and you can write your own tests for the "more stuff" you need.
I suspect this is the same underlying issue that affects django-registration in that the test runner only imports the URLs of the app being tested at the time -- ie, contrib.auth and not myapp There are various tickets about things similar to this issue but a quick scan of them implies the solution is to decouple things, which isn't going to be viable for your solution I'm guessing.
Another way around it would be to use Fabric file or Makefile to trigger a subset of tests, avoiding the two that fail because of your monkeypatch, and then add two alternate ape-friendly tests to replace them.

Django preview, TypeError: 'str' object is not callable

I'm trying to make a Preview function. I'm reading this blog, Django Admin Preview, but now I have the following error and I don't know what it means.
Traceback (most recent call last):
File "/home/user/webapps/django/lib/python2.5/django/core/handlers/base.py", line 92, in get_response
response = callback(request, *callback_args, **callback_kwargs)
TypeError: 'str' object is not callable
I'm lost..
Edit:
Thanks guys/gals, here is my view.py and url.py:
from diligencia.diligencias.views import preview
url(r'^admin/diligencias/diligencia/(?P<object_id>\d+)/preview/$','preview'),
(r'^admin/(.*)', admin.site.root),
from diligencia.diligencias.models import Diligencia
#staff_member_required
def preview(request, object_id):
return object_detail(request, object_id=object_id,queryset=Diligencia.objects.all(), template_object_name = 'diligencia_detail.html', )
The signature for the url function within a urlconf is like this:
def url(regex, view, kwargs=None, name=None, prefix='')
You are using positional parameters only, but are passing only regex, view and name. So Python thinks your third parameter is the kwargs dictionary, not the name.
Instead, do this:
url(r'^admin/diligencias/diligencia/(?P<object_id>\d+)/preview/$', name='preview'),
to pass the name as a kwarg so that Python recognises it properly.
I suspect your view isn't a function. Make sure the argument in your urls.py is a function that takes one parameter. Like :
import default
url(r'^s(?:ite)?/search$', default.search, name="search"),
And then you have in default.py
def search(request) :
# do stuff