Each time a request is made to my app, I am using middleware to retrieve and store a 'Page' object which has information relevant that particular page. I am storing a reference to this object in the request object, here is an example:
class PageMiddleware(object):
def process_request(self, request):
if not hasattr(request, 'page'):
request.page = Page.objects.get(slug=<slug>)
return None
It works well enough, but I want to access this object in a template tag. Template tags only have a reference to 'context' thought, meaning I am unable to see my Page object.
I know I can use a custom context processor for this but that means modifying the settings file further and I'd like to try and keep this app as encapsulated as possible. I noticed, for example, that the Debug Toolbar app manages to append data to the template context without modifying the TEMPLATE_CONTEXT_PROCESSORS.
In short, I want to access my Page object in a template tag ideally by just using middleware. Any ideas?
Edit: I am using a standard template tag, with the following class:
class GetPageContentNode(Node):
def __init__(self, key):
self.key = key
def render(self, context):
return context['request'].page
Have a look at this, you can get access to the request object (and your object) by passing takes_context when registering the template tag
Access request in django custom template tags
Have a search for "takes_context" on this page:
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#registering-the-tag
Call every render_to_response with a context_instance parameter, like:
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
EDITED as sugested by Daniel Roseman:
And add django.core.context_processors.request to your TEMPLATE_CONTEXT_PROCESSORS settings.
Try this:
class GetPageContentNode(Node):
def __init__(self, key):
self.key = key
def render(self, context):
request = template.Variable('request').resolve(context) # here's the magic!
return request.page
Related
I try to access post request and check if user with email that is in post request already exist. When I try to send data to endpoint, I get error
TypeError: object() takes no parameters
my views.py
#csrf_exempt
class CheckIfEmailAvailable():
#csrf_exempt
def check(request):
email = request.POST.get("email")
if User.objects.filter(email=email).exists():
return Response({'status': 'not available'})
my url.py
url(r'^api/checkmail/', CheckIfEmailAvailable, name='check'),
What am I doing wrong ?
In this case better to use function in the url
from your_app.views import check
url(r'^api/checkmail/', check, name='check')
and your view will be like this (only function)
#csrf_exempt
def check(request):
email = request.POST.get("email")
if User.objects.filter(email=email).exists():
return JsonResponse({'status': 'not available'})
Also FYI if you want to use #csrf_exempt with classes you should use dispatch, you can get more info here
Example of JsonResponse
from django.http import JsonResponse
def your_view(request):
return JsonResponse({'foo':'bar'})
You need to inherit your class your generic django views for this to work. Url patterns expect a callable that can accept request (with args and kwargs) and return response. View.as_view class method returns a callable for you.
# In views file
from django.views.generic import View
...
class CheckIfEmailAvailable(View):
def get(self, request, *args, **kwargs):
return self.check(request) # or something else depends on your use case
# In urls file
from django.views.decorators.csrf import csrf_exempt
...
url(r'^api/checkmail/', csrf_exempt(CheckIfEmailAvailable.as_view()), name='check'),
Moreover csrf_exempt decorator won't work (out of box) on classes, neither on bound methods for that matter. Better way should be to use them in urls.
If you had posted the full traceback, you would see that the error does not come from parsing the request; Django does not even get nearly that far.
You can't use a class as a view like that. You should have check as a standalone function, without a class, and refer to it directly in your urls.
Django does support class-based views, but they have a very specific structure, need to inherit from the proper base class, and be referenced in the urls.py in a specific way.
I have a custom paginator that needs to access the kwargs argument pk given a url like this one.
r'^(?P<pk>[0-9]+)/stuff/$'
Here's what I am doing right now, but it feels a little hackish.
This is my view.
class StuffList(ListAPIView):
serializer_class = MySerializer
pagination_class = MyPagination
def get_queryset(self):
self.request.kwargs = self.kwargs
return Stuff.objects.all()
and then, in my paginator, when the request object is passed as an argument to paginate_queryset, I can access the kwargs through it.
def paginate_queryset(self, queryset, request, view=None):
"""Returns paginated queryset."""
self.pk = request.kwargs.get('pk')
pass
Is there another way to access it from the request object without manually adding the kwargs to it in get_queryset? I find it odd, because i can easily get a ton of info with the request object, but can't seem to be able to simply get the url kwargs, which forces me to add them myself. Thank you!
Based on the documentation it's valid to access the kwargs on the instance object: http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-the-url
Essentially, I'm trying to find a good way to attach more views to a Router without creating a custom Router. What's a good way to accomplish this?
Here is something sort of equivalent to what I'm trying to accomplish. Variable names have been changed and the example method I want to introduce is extremely simplified for the sake of this question.
Router:
router = routers.SimpleRouter(trailing_slash=False)
router.register(r'myobjects', MyObjectViewSet, base_name='myobjects')
urlpatterns = router.urls
ViewSet
class MyObjectsViewSet(viewsets.ViewSet):
""" Provides API Methods to manage MyObjects. """
def list(self, request):
""" Returns a list of MyObjects. """
data = get_list_of_myobjects()
return Response(data)
def retrieve(self, request, pk):
""" Returns a single MyObject. """
data = fetch_my_object(pk)
return Response(data)
def destroy(self, request, pk):
""" Deletes a single MyObject. """
fetch_my_object_and_delete(pk)
return Response()
One example of another method type I need to include. (There are many of these):
def get_locations(self, request):
""" Returns a list of location objects somehow related to MyObject """
locations = calculate_something()
return Response(locations)
The end-result is that the following URL would work correctly and be implemented 'cleanly'.
GET example.com/myobjects/123/locations
The answer given by mariodev above is correct, as long as you're only looking to make GET requests.
If you want to POST to a function you're appending to a ViewSet, you need to use the action decorator:
from rest_framework.decorators import action, link
from rest_framework.response import Response
class MyObjectsViewSet(viewsets.ViewSet):
# For GET Requests
#link()
def get_locations(self, request):
""" Returns a list of location objects somehow related to MyObject """
locations = calculate_something()
return Response(locations)
# For POST Requests
#action()
def update_location(self, request, pk):
""" Updates the object identified by the pk """
location = self.get_object()
location.field = update_location_field() # your custom code
location.save()
# ...create a serializer and return with updated data...
Then you would POST to a URL formatted like:
/myobjects/123/update_location/
http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing has more information if you're interested!
You can now do this with the list_route and detail_route decorators: http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
For example:
from rest_framework.decorators import list_route
from rest_framework.response import Response
...
class MyObjectsViewSet(viewsets.ViewSet):
...
#list_route()
def locations(self, request):
queryset = get_locations()
serializer = LocationSerializer(queryset, many=True)
return Response(serializer.data)
You define method like you do now, but you need to use the same url as method name and add link decorator, so for
/myobjects/123/locations/
You add method like this
#link(permission_classes=[...])
def locations(self, request, pk=None):
...
and router will pick it automatically.
From Routing to extra methods on a ViewSet:
I think you may need to route the method by hand, i.e. The Old-Fashioned Way™.
First pull the method out as a separate view:
set_password_view = UserViewSet.as_view({'post': 'set_password'})
(or such)
Then assign your URL:
url(r'^users/username_available/$', set_password_view, name-=...)
(Or such)
There's a related question on SO.
If you want to extend a viewset with a view that is or should not directly be written inside your viewset, you can write a “wrapper” action to pass the data through.
For example, with class based views:
from somewhere import YourExternalClassView
class SomeViewSet(viewsets.ReadOnlyModelViewSet):
# ...
#action(detail=True)
def your_action(self, request, pk):
return YourExternalClassView.as_view()(request, pk=pk)
How does it work?
On class based views, the as_view method returns a view function, to which we will pass the data we received from the action. The view will then hand over to process further.
For non-class based view, the views can be called/wrapped directly without .as_view(...)(...).
In a Django application working with recipes I have subclassed DeleteView to create my IngredientListItemDeleteView, but I would like the result of get_success_url to depend on a property of the item that was just deleted.
I would like to do something like this:
def get_success_url(self):
item = get_object_or_404(IngredientListItem, pk=self.kwargs['pk']) # -> 404
return this_item.recipe.get_absolute_url()
I understand that I get a 404 error because the item in question no longer exists but I have had no luck storing the relevant information about the item (namely, its containing recipe) before it gets deleted. For instance, if I put into the get method any code like
self.success_url = get_object_or_404(IngredientListItem,
pk=self.kwargs['pk']).recipe.get_absolute_url()
then by the time success_url is looked at (after deletion), it has the value None.
How can I make my success URL depend on this property of the deleted item?
In Django 1.6, the delete method has been changed so that the get_success_url method is called before the object is deleted.
def delete(self, request, *args, **kwargs):
"""
Calls the delete() method on the fetched object and then
redirects to the success URL.
"""
self.object = self.get_object()
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)
I recommend you override your delete method as above, until you upgrade to Django 1.6. If you need to do this for multiple classes, you could create a mixin.
Note that you don't need to fetch the item from the database with get_item_or_404 -- you can access it in your get_success_url method as self.object.
I'm using Django's class based DetailView generic view to look up an object for display. Under certain circumstances, rather than displaying the object, I wish to back out and issue a HTTP rediect instead. I can't see how I go about doing this. It's for when a user hits an object in my app, but without using the canonical URL. So, for example, on StackOverflow URLs take the form:
http://stackoverflow.com/<content_type>/<pk>/<seo_friendly_slug>
eg:
http://stackoverflow.com/questions/5661806/django-debug-toolbar-with-django-cms-and-django-1-3
You can actually type anything as the seo_friendly_slug part and it will redirect you to the correct canonical URL for the object looked up via the PK.
I wish to do the same in my DetailView. Retrieve the object, check that it's the canonical URL, and if not redirect to the item's get_absolute_url URL.
I can't return an HttpResponseRedirect in get_object, as it's expecting the looked up object. I can't seem to return it from get_context_data, as it's just expecting context data.
Maybe I just need to write a manual view, but I wondered if anyone knew if it was possible?
Thanks!
Ludo.
This isn't a natural fit for DetailView. To do this you need to override the get method of BaseDetailView, which looks like:
class BaseDetailView(SingleObjectMixin, View):
def get(self, request, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
So in your class you'd need to provide a new get method which did the URL check between fetching the object and setting up the context. Something like:
def get(self, request, **kwargs):
self.object = self.get_object()
if self.request.path != self.object.get_absolute_url():
return HttpResponseRedirect(self.object.get_absolute_url())
else:
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
As you end up overriding so much of the functionality it becomes questionable whether it's worth actually using a generic view for this, but youknow.
Developing on Rolo's answer and comments, I came up with the following generic view to serve this purpose:
from django import http
from django.views import generic
class CanonicalDetailView(generic.DetailView):
"""
A DetailView which redirects to the absolute_url, if necessary.
"""
def get_object(self, *args, **kwargs):
# Return any previously-cached object
if getattr(self, 'object', None):
return self.object
return super(CanonicalDetailView, self).get_object(*args, **kwargs)
def get(self, *args, **kwargs):
# Make sure to use the canonical URL
self.object = self.get_object()
obj_url = self.object.get_absolute_url()
if self.request.path != obj_url:
return http.HttpResponsePermanentRedirect(obj_url)
return super(CanonicalDetailView, self).get(*args, **kwargs);
This is used in the same manner as the normal DetailView, and should work for any model which implements get_absolute_url correctly.