I'm trying to use single url with two views. I found an example of that on official django documentation. I have something like this:
class DetailOrderView(View):
"""Combines form and detail parts into one and in darkness binds
them."""
# import pdb; pdb.set_trace()
def get(self, request, *args, **kwargs):
view = DisplayDetailOrderView.as_view()
return view(self, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = FormDetailOrderView.as_view()
return view(self, *args, **kwargs)
And in urls.py I refer to DetailOrderView.as_view()
However, when I try to run this I get this error:
.... lib/python3.6/site-packages/django/views/generic/base.py", line 84, in dispatch
if request.method.lower() in self.http_method_names:
AttributeError: 'DetailOrderView' object has no attribute 'method'
Which basically means when dispatch(self, request, *args, **kwargs) tries to call request.method.lower() it finds that request has no attribute method! Which is kinda strange right?
request should be defined in as_view() right?
This is pretty much c/p from the official documentation and it should work so I must be doing something stupid..
You are correct that you need to use .as_view() first, but you call the view
function with self as first argument. The first argument of a view function is the request, so:
class DetailOrderView(View):
"""Combines form and detail parts into one and in darkness binds
them."""
def get(self, request, *args, **kwargs):
view = DisplayDetailOrderView.as_view()
return view(request, *args, **kwargs) # no self
def post(self, request, *args, **kwargs):
view = FormDetailOrderView.as_view()
return view(request, *args, **kwargs) # no self
Note that this still can go wrong, since if the DisplayDetailOrderView for example has other URL parameters, it is possible that this view can not process the data correctly.
Although calling another view is technically possible, is is not very common.
Related
I'm attempting to write a mixin for setting a translation language based on the language set in the user Profile model.
When a get request comes in, the mixin should set a language to user language, get response from the view that adds the mixin, and then set the language back to what it was before. I wrote the following mixin, which is invoked, but it's get method is not invoked. What am I doing wrong?
class SetUserLanguageMixin(object):
def get(self, request):
current_language = translation.get_language()
try:
translation.activate(request.user.profile.language)
response = super(SetUserLanguageMixin, self).get(request)
finally:
translation.activate(current_language)
return response
class SomeView(LoggingMixin, APIView, SetUserLanguageMixin):
def get(self, request):
...
return Response(data, status=status.HTTP_200_OK)
If your SomeView overrides get(), then your mixin's get() method will not be called unless you call super(). You could try overriding dispatch in your mixin instead.
Note that your view will be more robust if the overridden get/dispatch method accepts args and kwargs:
def dispatch(self, request, *args, **kwargs):
...
response = super(SetUserLanguageMixin, self).dispatch(request, *args, **kwargs)
...
My problem is how to write more strict API using more generic REST API. I have working api in my application but I need to add some more strict services based on generic API and here is a problem because I can't simple override request data because it's immutable. I'm using Django rest framework 3.
Example:
My generic api for animals:
class AnimalService(APIView):
def get(self, request, *args, **kwargs):
data = request.data.copy()
if data.get('type') == 'dog':
#...do something
Now I need api only for hardcoded dogs:
class DogService(AnimalService):
def get(self, request, *args, **kwargs):
#hardcode request.data['type'] = 'dog'
return super(DogService, self).get(*args, **kwargs)
Instead of overriding the request object, you can pass the type in kwargs. You can then use these kwargs in AnimalServiceView as these modified kwargs are passed to it.
class DogService(AnimalService):
def get(self, request, *args, **kwargs):
kwargs['type'] = 'dog' # set the animal type in kwargs
return super(DogService, self).get(request, *args, **kwargs)
Then in your AnimalService view, you can do:
class AnimalService(APIView):
def get(self, request, *args, **kwargs):
if kwargs.get('type') == 'dog': # check the 'type'
#...do something
Another way is to write a custom middleware which will set the animal_type attribute on the request depending on the requested resource. Then in your views, you can just check using request.animal_type attribute.
I have a decorator:
def site_login_required(function=None):
def _dec(view_func):
def _view(request, *args, **kwargs):
gets site_id. checks stuff. if else.
This is called in the normal way:
#site_login_required
def frontPage(request, url_arg):
The decorator finds a site_id. It would be nice to have this available in the frontPage function. Is that possible? If so how?
You could pass it in as a kwarg when you call the view function within the decorator:
def site_login_required(function=None):
def _dec(view_func):
def _view(request, *args, **kwargs):
site_id = get_site_id()
url_arg = get_a_url()
view_func(request, url_arg, site_id=site_id)
#site_login_required
def frontPage(request, url_arg, site_id=None, *args, **kwargs):
...
As a side note, it is useful to accept *args and **kwargs in all view functions, so they consume extra arguments instead of choking with decorators like this wrapped around them.
right now I'm using this app for permission checking: django-rules
However it hasn't been updated for over a year now and there's no decorator for the "new" (since django 1.3) class based views. I would like to be able to use at the urls.py like this:
url(r'^casos/(?P<pk>\d+)/editar/$', rules_permission_required('lawsuits.logical_check', raise_exception=True)(CaseUpdateView.as_view()), name='case_edit'),
I can't figure out how to get the object from the class based view from the decorator. Do you guys have any idea? Here's what I have so far:
from django.utils.decorators import available_attrs
def rules_permission_required(perm, queryset=None, login_url=None, raise_exception=False):
def wrapper(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def inner(request, *args, **kwargs):
#view_func is the class based view -> <function MyEditView at 0x94e54c4>
print view_func.get_object() # doesnt work
print view_func(request, *args, **kwargs).get_object() # doesnt work either
#any ideas?
if not request.user.has_perm(perm, obj=obj):
return redirect_to_login(request, login_url, raise_exception)
return view_func(request, *args, **kwargs)
return inner
return wrapper
Many thanks in advance!
Use method_decorator on the dispatch() method: https://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating-class-based-views
from django.utils.decorators import method_decorator
class ClassBasedView(View):
#method_decorator(rules_permission_required)
def dispatch(self, *args, **kwargs):
return super(ClassBasedView, self).dispatch(*args, **kwargs)
Or you could decorate the output of the as_view class method, either in your url config (as described in the link above), or by saving the instance into a variable.
class ClassBasedView(View):
def dispatch(self, *args, **kwargs):
return super(ClassBasedView, self).dispatch(*args, **kwargs)
class_based_view = rules_permission_required(ClassBasedView.as_view())
Though I'm not quite sure whether the last example could cause thread safety problems (depends on how Django handles the instances). The best way is probably to stick with the method_decorator.
I ended up using a class decorator
def rules_permission_required(perm, queryset=None, login_url=None, raise_exception=False):
def wrapper(cls):
def view_wrapper(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def inner(self, request, *args, **kwargs):
# get object
obj = get_object_from_classbased_instance(
self, queryset, request, *args, **kwargs
)
# do anything you want
return inner
cls.dispatch = view_wrapper(cls.dispatch)
return cls
return wrapper
So, I'm pretty familiar with wrapping functions in views.py. So I've written a decorator to redirect to the default REDIRECT_URL if the user is logged in (sort of a reverse login_required); it's based on how I've made views in the past:
def not_logged_in(redirect_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
def decorator(view_func, *args, **kwargs):
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated():
return view_func(*args, **kwargs)
else:
redirect_url = (request.REQUEST.get(redirect_field_name, redirect_url) or
settings.REDIRECT_URL)
return HttpResponseRedirect(redirect_url)
return wrapper
return decorator
However, I get the following error: 'function' object has no attribute 'status_code' which is caused by a MiddleWare expecting an HttpResponse. When I look at the value for response, I see that it's <function wrapper at 0x2b3a9922a500>.
Here's how I'm calling it in urls.py:
url(r'login/',
not_logged_in(auth_views.login),
{'authentication_form': LoginForm },
),
Here’s my implementation of the same thing.
def logout_required(view):
def f(request, *args, **kwargs):
if request.user.is_anonymous():
return view(request, *args, **kwargs)
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
return f
In urls.py:
urlpatterns = patterns("",
url(r"^login/", logout_required(login), {"template_name": "users/login.html"}, "login"),
# ...
I hope this helps (unsure though).
The first argument to a decorator should be the function that needs to be decorated.
def not_logged_in(func, redirect_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
The decorator function is also not needed. Return the wrapper function from not_logged_in.
The way you've implemented your decorator, it is parameterised and therefore callable: that's why you've got the extra level of function that fizixx wrongly says is not required. You need to call the outer wrapper initially, in order to return the actual decorated function. So something like:
url(r'login/',
not_logged_in(auth_views.login)('/redirect/', 'redirect_field'),
{'authentication_form': LoginForm },
),