I am trying to add the decorator #xframe_options_exempt into a django template view but it complais with a
Exception Value: 'dict' object has no attribute
'xframe_options_exempt'
I noticed in Django 1.9 docs the decorator is used for views with a request parameter and I am using a TemplateView.
Is it possible to use it like this?
class MyView(TemplateView):
"""
"""
template_name = 'app/template.html'
from django.views.decorators.clickjacking import xframe_options_exempt
#xframe_options_exempt
def get_context_data(self, **kwargs):
context = {}
context['test'] = 'hello'
return context
Basically I need to embed a django template view into an iframe
When you are decorating class based views, you should use method_decorator. You should override a method that takes the request as an argument, e.g. dispatch (will apply to all request types) or get (will apply to get requests but not post requests). As you found, decorating get_context_data will not work.
class MyView(TemplateView):
#method_decorator(xframe_options_exempt):
def dispatch(self, *args, **kwargs):
return super(MyView, self).dispatch(*args, **kwargs)
Note that by using super() you don't have to duplicate the code from TemplateView.
You can decorate the class if you prefer (Django 1.9+)
#method_decorator(xframe_options_exempt, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
Based on the available #xframe_options_exempt decorator, you can also implement a mixin class to be mixed into your view classes:
class XFrameOptionsExemptMixin:
#xframe_options_exempt
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
class SomeView(XFrameOptionsExemptMixin, TemplateView):
…
Well, if anyone else has this problem, this decorator cannot be applied to get_context_data method, but you can override the get method from the TemplateView, something like this:
class MyView(TemplateView):
"""
"""
template_name = 'app/template.html'
from django.views.decorators.clickjacking import xframe_options_exempt
#xframe_options_exempt
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
def get_context_data(self, **kwargs):
context = {}
context['test'] = 'hello'
return context
And this will do the trick
Related
I want to switch between two models depending on which route I am in.
I am overwriting get_queryset() function to return the correct model:
class DynamicModelView(TemplateView, PageDescriptionListingMixin):
model = None
template_name = 'dynamic_model.html'
context_object_name = "accounts"
def get_context_data(self, **kwargs):
context = super(DynamicModelView, self).get_context_data(**kwargs)
self.add_page_text_to_context(context)
return context
def get_queryset(self):
if '/dynamic_user/' in self.request.path:
model = UserAccount
else:
model = AdminAccount
return model.objects.first()
As you can see in get_context_data I am injecting an object in context for AdminAccount but inside template I can't see it! in fact if I changed model from None to AdminAccount then it appears which I want that to happen dynamically.
Is there any way to switch models dynamically in Django?
override dispatch method.
def dispatch(self, request, *args, **kwargs):
if not self.model:
self.model = <MODEL>
return super(DynamicModelView, self).dispatch(request, *args, **kwargs)
I'm rewriting my django function view to class based views. I have this current function
#login_required
def settings(request, template_name="settings.html"):
context = {}
context['kcs'] = KlarnaProfile.objects.filter(user_profile__user=request.user)
context['extends'] = ExtendProfile.objects.filter(user_profile__user=request.user)
context['fortnoxs'] = FortnoxProfile.objects.filter(user_profile__user=request.user)
return render(request, template_name, context)
that confirms first if a user is logged in and then get's information linked to that user account
here's what I've got as my class based view
class SettingsView(TemplateView):
template_name = "settings.html"
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
how can I add the three filters that use the logged in user as a filter?
Use get_context_data method like this:
class SettingsView(TemplateView):
...
def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
context_data['kcs'] = KlarnaProfile.objects.filter(user_profile__user=self.request.user)
...
return context_data
Neat pick. Instead of doing:
class SettingsView(TemplateView):
...
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
You can do:
#method_decorator(login_required, name='dispatch')
class SettingsView(TemplateView):
...
You might want to read Decorating the class from Django's official documentation.
I noticed that I am setting site-wide context variables and request variables for many views on my site. Naturally, this situation calls for inheritance. If all of my view class-based views are inheriting from SiteView instead of the generic View, I can factor out all the commonalities into the SiteView child class. I can then inherit from SiteView on all my views. But, I cannot get this to work. Here is my code:
from django.contrib.auth.decorators import login_required
from django.views.generic import View
from django.utils.decorators import method_decorator
class SiteView(View):
''' Extends the generic django-supplied View class '''
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(SiteView, self).dispatch(*args, **kwargs)
def get(self, *args, **kwargs):
''' Adds the variables required in the get request '''
context = super(SiteView, self).get(*args, **kwargs)
context['common_var'] = 'some common value'
context['user'] = request.user
return self.render_to_response(context)
This throws the following TypeError:
dispatch() missing 1 required positional argument: 'request'
Any help would be appreciated
Edit: Even though the correct answer is marked, there were other issues with the code. In particular, the get method of the SiteView should not have the following line:
context = super(SiteView, self).get(*args, **kwargs)
This is because the View class does NOT have any get method.
You forgot to pass the request to the super().dispatch(..) call:
class SiteView(View):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(SiteView, self).dispatch(request, *args, **kwargs)
or you can just omit the request in the dispatch parameters, and thus pass it through *args and **kwargs:
class SiteView(View):
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(SiteView, self).dispatch(*args, **kwargs)
It is however probably more elegant, to pass the name of the function, like:
#method_decorator(login_required, name='dispatch')
class SiteView(View):
# ...
EDIT: Note that a View has no get(..), post(..), etc. method. The dispatch(..) method will look if such method exists, and if so redirect to it. If such method does not exists, it will return a "405 Method Not Allowed" response.
Your get(..) function thus be implemented like:
#method_decorator(login_required, name='dispatch')
class SiteView(View):
''' Extends the generic django-supplied View class '''
def render_to_response(self, context):
# ...
def get(self, request, *args, **kwargs):
context = {
'common_var': 'some common value',
'user': request.user
}
return self.render_to_response(context)
It perhaps makes more sense to implement a "mixin" (perhaps with a subclass of the LoginRequiredMixin mixin [Django-doc].
For example like:
class SiteViewMixin(LoginRequiredMixin):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context.update(common_var='some common value', user=self.request.user)
return context
and then use the mixin in another view, like:
class SomeView(SiteViewMixin, TemplateView):
# ...
I have this CBV:
class GetStuff(View):
def get(self, request, company_id):
...
I want to decorate the get function with a custom function which takes the request and company_id arguments and check some permissions.
Any idea on how to achieve this? Most information I've found about decorators focus on FBV.
This is what I have so far:
def custom_decorator(func_view):
def wrapper(request, company_id):
if not request.user.is_staff:
# do_something()
return func_view(request, company_id)
return wrapper
You have to decorate the dispatch method
To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the dispatch() method of the class. django doc
from django.utils.decorators import method_decorator
class GetStuff(View):
#method_decorator(custom_decorator)
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
or
#method_decorator(custom_decorator, name='dispatch')
class GetStuff(View):
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
you can find other decorating techniques in the docs
I've seen a lot of answers about how this is solved when not using Class Based Views. Is there some really obvious thing I'm missing about doing it with CBVs?
Basically I want to have a MultipleChoiceField in my form which has choices determined by what is happening in the view. e.g. I use the PK from the URL to do some backend requests and then those should be used to populate the choices.
# forms.py
from django.forms import Form, MultipleChoiceField, CharField
class EmailForm(Form):
users = MultipleChoiceField(required=False)
subject = CharField(max_length=100)
message = CharField()
def __init__(self, users=None, *args, **kwargs):
super(EmailForm, self).__init__(*args, **kwargs)
if users:
self.fields['users'].choices = users
#urls.py
from django.conf.urls import url, patterns
from .views import EmailView
# url patterns
urlpatterns = patterns('',
url( r'^(?P<pk>\d+)$', EmailView.as_view(), name="maindex" ),
)
#views.py
from django.views.generic import FormView, TemplateView
from .forms import EmailForm
class EmailView(FormView):
template_name = 'myapp/email.html'
form_class = EmailForm
success_ulr = '/thanks/'
def form_valid(self, form):
# Do stuff here
return super(EmailView, self).form_valid(form)
Basically it boils down to how/where to call the init function from the view. How do I do that? Or is there another way I've missed? I thought of overriding get_form_kwargs in the view, but couldn't make that do anything.
Thanks
The view:
from django.views.generic import FormView
class EmailView(FormView):
# ...
def get_form_kwargs(self):
kwargs = super(EmailView, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['users'] = users
return kwargs
The form:
from django import forms import Form
class EmailForm(Form):
users = MultipleChoiceField(required=False)
# ...
def __init__(self, *args, **kwargs):
self.users = kwargs.pop('users', None)
super(EmailForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = self.users
Basically, what I've done in a similar case is the following (Python 3.5, Django 1.8):
def get_form(self, *args, **kwargs):
form= super().get_form(*args, **kwargs)
form.fields['rank'].choices= <sequence of 2-tuples>
return form
where obviously rank is the field name. This way I use the default form.
Alright, the FormMixin calls get_form to get the form-class which looks like
def get_form(self, form_class):
"""
Returns an instance of the form to be used in this view.
"""
return form_class(**self.get_form_kwargs())
So you can either override get_form to instance your Form yourself
def get_form(self, form_class):
return EmailForm(files=self.request.FILES or None,
data=self.request.POST or None,
users=some_user_queryset)
or stay a bit more generic and override get_form_kwargs to something like
def get_form_kwargs(self):
form_kws = super(EmailView, self).get_form_kwargs()
form_kws["users"] = some_user_queryset
return form_kws
One way to do it is:
class EmailView(FormView):
# ...
def get(self, request, *args, **kwargs):
self.users = ...
return super(EmailView, self).get(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(EmailView, self).get_form_kwargs()
kwargs['users'] = self.users
return kwargs
This allows you to set the user choices in the view and to pass them to the form.
You can override get_form.
I needed to update the choices for ChoiceField based on logged in user.
Form:
class InteractionCreateForm(forms.Form):
device = forms.ChoiceField(choices=[(None, '----------')])
...
View:
class InteractionCreateView(FormView):
form_class = InteractionCreateForm
...
def get_form(self, form_class=None):
form_class = super().get_form(form_class=None)
form_class.fields['device'].choices = \
form_class.fields['device'].choices \
+ [(device.pk, device) for device in Device.objects.filter(owner=self.request.user.id)]
return form_class