After saving an object by clicking saveasnew button in admin interface i want to redirect to another url:
admin.py
class AirplanesAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
if "_saveasnew" in request.POST:
self.message_user(request, 'YAY!!!')
return HttpResponseRedirect(reverse('admin:routes_airplanes_change',
args=(obj.pk,),
current_app=self.admin_site.name))
The "YAY" message is showed... but django redirects me in a list of pbject pages, not to a new object page... Don't work even this code:
admin.py
class AirplanesAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
if "_saveasnew" in request.POST:
self.message_user(request, 'YAY!!!')
redirect('http://example.com')
I see "YAY", but it redirects me to a list object again... what am I doing wrong ?
save_model return None. and It's result is not used anywhere. So doesn't matter what is returned (redirect(...) or None)
I think
def response_add(self, request, new_object):
pass
and
def response_change(self, request, new_object):
pass
are what You are looking for.
If we look into django source code we will see following:
def response_change(self, request, obj):
"""
Determines the HttpResponse for the change_view stage.
"""
and
def response_add(self, request, obj, post_url_continue=None):
"""
Determines the HttpResponse for the add_view stage.
"""
Related
My views.py have a mix of def and ClassViews:
#login_required(login_url='login')
#allowed_users(allowed_roles=['Admin', 'Staff', 'Lite Scan'])
def litescan(request):
filteredOutput = Stock.objects.all()
val = {}...
#method_decorator(login_required(login_url='login'), name='dispatch')
class HomeView(ListView):
model = Post
template_name = 'community.html'
ordering = ['-id']
And here's my decorators.py if that is helpful:
from django.shortcuts import redirect
from django.http import HttpResponseRedirect
def unauthenticated_user(view_func):
def wrapper_func(request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('home')
else:
return view_func(request, *args, **kwargs)
return wrapper_func
def allowed_users(allowed_roles=[]):
def decorator(view_func):
def wrapper_func(request, *args, **kwargs):
group = None
if request.user.groups.exists():
group = request.user.groups.all()[0].name
if group in allowed_roles:
return view_func(request, *args, **kwargs)
else:
url = ('/forbidden')
return HttpResponseRedirect(url)
return wrapper_func
return decorator
I found out that #login_required and #allowed_users give out an error when used with ClassView. So i used #method_decorator which brings me to the login page before redirecting to the page. However, I can not find a way to restrict access to only certain groups like Admin, Staff, Lite Scan with my ClassView.
Little help will be appreciated. Thanks!
You can use AccessMixin for your class views.
Example I found:
from django.contrib.auth.mixins import AccessMixin
from django.http import HttpResponseRedirect
class FinanceOverview(AccessMixin, TemplateMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
# This will redirect to the login view
return self.handle_no_permission()
if not self.request.user.groups.filter(name="FinanceGrp").exists():
# Redirect the user to somewhere else - add your URL here
return HttpResponseRedirect(...)
# Checks pass, let http method handlers process the request
return super().dispatch(request, *args, **kwargs)
More info found here: Use LoginRequiredMixin and UserPassesTestMixin at the same time
Relying on Django Permissions may be a far simpler approach to giving access to such a view. Rather than checking for a specific list of groups, you can assign permissions to those groups and give access to the view based on whether the user's groups have the appropriate permissions.
views.py
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionsRequiredMixin
#permission_required('foo.view_bar')
def my_view(request):
...
class MyView(PermissionRequiredMixin, DetailView):
permission_required = ('foo.view_bar', )
...
I use a decorator to check whether the user is logged in, and redirect to the login page and remind the user. I want to use context parametric rendering template. How can I write the code?
def check_login(func):
def wrapper(request, *args, **kwargs):
if request.session.get("unique_id"):
return func(request, *args, **kwargs)
else:
context = {'title': 'login', 'not_login': 1}
return redirect(reverse('login'), context=context)
return wrapper
# view.py
def login(request):
context = {'title': 'login', 'not_login': 0}
return render(request, 'login.html', context)
The code like this. If user get the login page, templates use not_login=0, if the request is from redirect, templates use not_login=1.
I am trying to specify a specific method of handling file uploads for a class based view. Per the docs this can be achieved by something like:
from django.core.files.uploadhandler import TemporaryFileUploadHandler
request.upload_handlers = [TemporaryFileUploadHandler(request=request)]
If i specify this in post method of a FormView like so:
def post(self, request, *args, **kwargs):
request.upload_handlers = [TemporaryFileUploadHandler(request=request)]
return super().post(self, request, *args, **kwargs)
I get:
AttributeError: You cannot set the upload handlers after the upload has been processed.
Variants like yield the same result:
def post(self, request, *args, **kwargs):
self.request.upload_handlers = [TemporaryFileUploadHandler(request=self.request)]
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
However when i do this in the get method this is ineffective:
def get(self, request, *args, **kwargs):
request.upload_handlers = [TemporaryFileUploadHandler(request=self.request)]
return super().get(self, request, *args, **kwargs)
If I upload a small file it still uses the default django.core.files.uploadhandler.MemoryFileUploadHandler.
What am I doing wrong?
EDIT
Also when i try to mirror what is suggested in the note, I get the same AttributeError:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
#csrf_exempt
def post(self, request, *args, **kwargs):
request.upload_handlers = [TemporaryFileUploadHandler(request=request)]
return self._post(request, *args, **kwargs)
#csrf_protect
def _post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Ok, finally got it to work (using the suggestions provided by #Alasdair). Setting a method decorator(crsf_exempt) on post is not engough it needs to be on dispatch. For anyone struggling with this in the future, it goes like this:
from django.views.generic import FormView
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt, csrf_protect
#method_decorator(csrf_exempt, 'dispatch')
class UploadDataSetView(FormView):
def post(self, request, *args, **kwargs):
request.upload_handlers = [TemporaryFileUploadHandler(request=request)]
return self._post(request)
#method_decorator(csrf_protect)
def _post(self, request):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Also it will fail if you remove the {% csrf_token %} from your template (which is what you want).
Because you cannot change upload handler in view as it is something that gets invoked prior to your view function.
Get shouldn't collect post parameters so it behaves accordingly.
Upload Handlers
When a user uploads a file, Django passes off the file data to an
upload handler – a small class that handles file data as it gets
uploaded. Upload handlers are initially defined in the
FILE_UPLOAD_HANDLERS setting, which defaults to:
["django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler"]
If you want different upload handler you can change FILE_UPLOAD_HANDLERS settings
If this is not enough you can write your custom upload handler
Edit:
Also, request.POST is accessed byCsrfViewMiddleware which is enabled
by default. This means you will need to use csrf_exempt() on your view
to allow you to change the upload handlers.
I'm trying to provide some additional context into the get() method in my FormView. I need get() because I need to run some logic first, check for a potential redirect. I also need access to the request object (because I need to check session data). Can't figure out how to do it. Simplified code below..
Attempt 1:
class LoginView(FormView):
template_name = 'members/login.html'
form_class = LoginForm
def get(self, request):
# check if to redirect
if self.request.session.get('user'):
return redirect('/dashboard/')
# render page with extra context
else:
context = super(LoginView, self).get(request)
context['message'] = self.request.session['message']
return context
No errors, but context does not come through in the template.
Attempt 2:
class LoginView(FormView):
template_name = 'members/login.html'
form_class = LoginForm
def get_context_data(self, request, **kwargs):
# check if to redirect
if self.request.session.get('user'):
return redirect('/dashboard/')
# render page with extra context
else:
context = super(LoginView, self).get_context_data(**kwargs)
context['message'] = self.request.session['message']
return context
Getting TypeError: get_context_data() takes exactly 2 arguments (1 given)
P.S. This work relates to a workaround Django's buggy messages middleware which seems to be working locally flawlessly but on live (Heroku) is not 100% reliable, renders on some pages only. Ugh, frustration setting in...
Ditch the request argument to the get_context_data method. You should also use the dispatch method to check if the user is logged in.
class LoginView(FormView):
template_name = 'members/login.html'
form_class = LoginForm
def dispatch(self, *args, **kwargs):
"""Use this to check for 'user'."""
if request.session.get('user'):
return redirect('/dashboard/')
return super(LoginView, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
"""Use this to add extra context."""
context = super(LoginView, self).get_context_data(**kwargs)
context['message'] = self.request.session['message']
return context
I have an add form for CalibrationCertificates in my django admin site. If I link to it from a non-admin template, Instrument_Detail.html, is it possible to pass context information as a default value to the add form.
That is, the only choice in the add form is which Instrument the certificate is for. As the link is already associated with an instrument, is there a way to pass that value, such that the add certificate form will default to the instrument the user came from?
My ModelAdmin is as follows:
class CertificateAdmin(admin.ModelAdmin):
exclude = ('issued_by', 'expires',)
def save_model(self, request, obj, form, change):
obj.issued_by = request.user
obj.expires= datetime.date.today() + datetime.timedelta(days=obj.instrument.kind.duration)
obj.save()
Not sure if I understand your question correctly but I think this is what you want:
def add_view(self, request, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['my_extra_content'] = self.something
return super(MyModelAdmin, self).add_view(request, form_url,
extra_context=extra_context)