I inherited a Django(1.5.1) project and I need to put one view behind a #login_required decorator. Here is what i have in views.py:
I got this snippet of code from here and it looks like its purpose is to allow someone to apply the #login_requireddecorator to a class
class LoginRequiredMixin(object):
"""
View mixin which verifies that the user has authenticated.
NOTE:
This should be the left-most mixin of a view.
"""
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
class PermissionRequiredMixin(object):
login_url = settings.LOGIN_URL
permission_required = None
raise_exception = False
redirect_field_name = '/workers/'
def dispatch(self, request, *args, **kwargs):
# Verify class settings
if self.permission_required == None or len(
self.permission_required.split(".")) != 2:
raise Error("'PermissionRequiredMixin' requires "
"'permission_required' attribute to be set.")
has_permission = request.user.has_perm(self.permission_required)
if not has_permission:
if self.raise_exception:
return HttpResponseForbidden()
else:
path = urlquote(request.get_full_path())
tup = self.login_url, self.redirect_field_name, path
return HttpResponseRedirect("%s?%s=%s" % tup)
return super(PermissionRequiredMixin, self).dispatch(
request, *args, **kwargs)
I then apply this to the view i want to add permissions to like so:
class RootWorkerView(LoginRequiredMixin, PermissionRequiredMixin, APIView):
renderer_classes = (WorkersJSONRenderer, JSONRenderer,
BrowsableAPIRenderer)
def get(self, request):
worker_list = rest_models.WorkerList(request)
serializer = WorkerListSerializer(worker_list)
return Response(serializer.data)
The APIView argument is a carry over, as before it was the only argument. Is this correct?
When run, I get nothing. The template for the view I want to secure shows up with no login prompt.
Relevant snippet from urls.py:
url(r'^workers/$', views.RootWorkerView.as_view(),
name='root_worker_view'),
url(r'^login/$', 'django.contrib.auth.views.login',
{'template_name': 'dashboard/login.html'}),
/login/ does work, and I can login successful, so that's not the issue.
I feel like #method_decorator(login_required) isnt doing its job. Any ideas?
You can add the decorator in the urls.py
from django.contrib.auth.decorators import login_required
url(r'^workers/$', login_required(views.RootWorkerView.as_view()))
This worked for me.
now you can use Django builtin LoginRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
https://docs.djangoproject.com/en/3.2/topics/auth/default/#the-loginrequired-mixin
Related
I have to redirect from one class-based View to another class-based View. I did it by:
return redirect('report')
but the suggestion was to redirect by View names. I did try this, but it doesn't work.
views.py:
class UploadView(View):
def get(self, request, *args, **kwargs):
Reservation.objects.all().delete()
template = "ReportApp/upload.html"
return render(request, template)
def post(self, request, *args, **kwargs):
# try:
csv_file = request.FILES['file']
data_set = csv_file.read().decode('UTF-8')
# setup a stream which is when we loop through each line we are able to handle a data in a stream
io_string = io.StringIO(data_set)
next(io_string)
for column in csv.reader(io_string, delimiter=',', quotechar="|"):
_ = Reservation.objects.update_or_create(
reservation_code=column[0],
checkin=column[1],
checkout=column[2],
flat=column[3],
city=column[4],
net_income=column[5],
)
# return redirect('report')
return redirect(ReportView.as_view())
upload_view = UploadView.as_view()
class ReportView(View):
def get(self, request, *args, **kwargs):
urls.py:
from .views import upload_view, report_view, city_commission_view, error_view
from django.urls import path
urlpatterns = [
path('upload', upload_view, name='upload'),
path('report', report_view, name='report'),
path('city_commission', city_commission_view, name='city_commission'),
path('error', error_view, name='error'),
]
any suggestions how to do this?
Not sure what you want to achieve and I do not see the code for class ReportView(View): def get(self, request, *args, **kwargs):
but perhaps you could try something like this:
from django.views.generic import RedirectView
class ReportView(RedirectView):
def get_redirect_url(self, *args, **kwargs):
url_params = self.kwargs
and play with that.
I am working on blog project in which I added an add post which add post now I want only the superuser can add post and that page is visible only to superuser.
1st Method
Views.py
class AddPostView(CreateView):
model = Post
template_name = 'MainSite/add_post.html'
fields = '__all__'
this is my current view I am able to achieve authenticate for superuser using 2nd method
2nd Method
class AddPostView(View):
def get(self,request):
if request.user.is_superuser == True:
return render(...)
else:
pass
How can I achieve the same result using 1st method.I tried using LoginRequiredMixin but nothing is happening . I just import LoginRequiredMixin and use it like this .
class Addpost(CreateView,LoginRequiredMixin):
...
Thanks in advance and advice will be helpful.
You can work with a UserPassesTestMixin mixin [Django-doc]:
from django.contrib.auth.mixins import UserPassesTestMixin
class AddPostView(UserPassesTestMixin, CreateView):
# …
def test_func(self):
return self.request.user.is_superuser
# …
You can encapsulate this in a mixin:
from django.contrib.auth.mixins import UserPassesTestMixin
class AdminRequiredMixin(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_superuser
and then use this mixin:
class AddPostView(AdminRequiredMixin, CreateView):
# …
def test_func(self):
return self.request.user.is_superuser
# …
Mixins should be put before the actual view in the inheritance hierarchy: otherwise these appear after the view in the method resolution order (MRO), and thus likely will not override the behavior (correctly).
class AddPostView(CreateView,LoginRequiredMixin):
model = Post
template_name = 'MainSite/add_post.html'
fields = '__all__'
def dispatch(self, request, *args, **kwargs):
if request.user.is_anonymous:
return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name())
elif request.user.is_superuser:
return render(.....)
else
return super(AddPostView, self).dispatch(request, *args, **kwargs)
Use method_decorator and user_passes_test to achieve this
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import user_passes_test
class AddPostView(View):
#method_decorator(user_passes_test(lambda u: u.is_superuser))
def post(self, *args, **kwargs):
pass
i want to delete a database content.bt after deletion utl goes to http://127.0.0.1:8004/login/delete_detail/6/ ..how can i redirect to success.html ie in the same page
class DeleteView(generic.TemplateView):
template_name = 'success.html'
success_url='/login/success'
def get_context_data(self, *args, **kwargs):
context = super(DeleteView,self).get_context_data(**kwargs)
did = self.kwargs['did']
q_obj = Quest.objects.filter(id=did)
q_obj.delete()
You should override get_success_url method. For example:
def get_success_url(self):
return reverse_lazy('delete-success')
Also, try to use named urls in your success_url
success_url = reverse_lazy('delete-success')
You can use get_success_url method:
from django.urls import reverse_lazy
class DeleteView(generic.TemplateView):
template_name = 'success.html'
success_url='/login/success'
def get_context_data(self, *args, **kwargs):
context = super(DeleteView,self).get_context_data(**kwargs)
did = self.kwargs['did']
q_obj = Quest.objects.filter(id=did)
q_obj.delete()
def get_success_url(self, **kwargs):
return reverse_lazy('delete_detail', kwargs = {'pk': self.kwargs['did']})
Also instead of TemplateView you can use DeleteView class:
class QuestDelete(DeleteView):
model = Quest
pk_url_kwarg = 'did'
def get_success_url(self, **kwargs):
return reverse_lazy('delete_detail', kwargs = {'pk': self.kwargs['did']})
To use url's name you need to add name argument to the url pattern inside urls.py file like this:
urlpatterns = [
path('delete_detail', views.delete_detail, name='delete_detail'),
]
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
I have a Login Class Based View which I want to include in all my pages.
Is there a way to include Class Based View as template context processor, or do we have any other way to include in all pages ?
class LoginView(RedirectAuthenticatedUserMixin, FormView):
form_class = LoginForm
template_name = "account/login.html"
success_url = None
redirect_field_name = "next"
def form_valid(self, form):
success_url = self.get_success_url()
return form.login(self.request, redirect_url=success_url)
def get_success_url(self):
# Explicitly passed ?next= URL takes precedence
ret = (get_next_redirect_url(self.request,
self.redirect_field_name)
or self.success_url)
return ret
def get_context_data(self, **kwargs):
ret = super(LoginView, self).get_context_data(**kwargs)
signup_url = passthrough_next_redirect_url(self.request,
reverse("account_signup"),
self.redirect_field_name)
redirect_field_value = self.request.REQUEST \
.get(self.redirect_field_name)
ret.update({"signup_url": signup_url,
"site": Site.objects.get_current(),
"redirect_field_name": self.redirect_field_name,
"redirect_field_value": redirect_field_value})
return ret
login = LoginView.as_view()
You can decorate the view:
Either in urls.py:
from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView
from .views import VoteView
urlpatterns = patterns('',
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
)
Or in views.py:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
Or, you can inherit from LoginRequiredMixin from django-braces:
from braces.views import LoginRequiredMixin
class Index(LoginRequiredMixin, TemplateView):
template_name = 'sekret.html'
All these examples show how to require authentication for class based views (which I believe is what you are trying to do). The first two are directly from the documentation.
Once you implement either of the above examples, your application will behave like this:
A user enters a URL that maps to one of the above views.
django middleware checks if the user is logged in.
If not, django will redirect to LOGIN_URL, by default its /accounts/login/, and add a ?next parameter, pointing to the original URL from #1.
The view from #3 will display a login form.
Once the user successfully authenticates, the view from #3 will update the session, and redirect the user to the URL from the next parameter.
If the user is logged in, then the decorated view will be executed as normal.