limit creation of objects in admin - django

Trying to limit my objects to 1 in admin pages. I get "newuserpath object with primary key u'add/foo' does not exist." It would be ok if it would just return without setting anything, but ideally with an error message. This is what I have in my admin.py.
from django.contrib import admin
from fileman.models import Newuserpath
from django.http import HttpResponseRedirect
class NewuserpathAdmin(admin.ModelAdmin):
def add_view(self, request):
if request.method == "POST":
# Assuming you want a single, global Newuserpath object
if Newuserpath.objects.count() > 0:
# redirect to a page saying
# you can't create more than one
return HttpResponseRedirect('foo')
return super(NewuserpathAdmin, self).add_view(request)
admin.site.register(Newuserpath, NewuserpathAdmin)
I'm following the best answer here: Is it possible to limit of object creation of a model in admin panel?
It just doesn't quite work.I tried using the other method by adding code in forms.py and importing it from there. But I'm unsure of how to use that in my admin.py.

The error is simply that you are using a relative path for your redirect - so the browser is adding 'foo' to the existing URL, admin/app/newuserpath/add/.
The best way to manage this is to use the URL reverse function - assuming you have given your error page URLconf the name 'newuserpath_error':
return HttpResponseRedirect(reverse('newuserpath_error'))

Related

Show user error message if form is not filled via HttpResponseRedirect

I'm trying to create the functionality so that a user is returned to the url with an error message if they don't fill in the forms.
My urls
from django.urls import path
from content import views
urlpatterns = [
path('readerpage/<int:content_id>', views.readerpage, name='readerpage'),
path('readerpage/<int:content_id>/add_review',
views.add_review, name='add_review'),
]
My view
def add_review(request, content_id):
content = get_object_or_404(Content, pk=content_id)
if request.POST['readability'] and request.POST['readability_rating'] and request.POST['actionability'] and request.POST['actionability_rating'] and request.POST['general_comments']:
review = Review()
review.readability = request.POST['readability']
review.readability_rating = request.POST['readability_rating']
review.actionability = request.POST['actionability']
review.actionability_rating = request.POST['actionability_rating']
review.general_comments = request.POST['general_comments']
review.avg = (float(review.readability_rating) +
float(review.actionability_rating)) / 2
review.content = content
review.save()
return redirect('home')
else:
return HttpResponseRedirect(reverse('readerpage', args=(content_id,)))
Right now the user gets returned but doesn't get an error message
I've tried with the return render, instead of the HttpResponseRedirect
return render (request, 'content/readerpage', {'error': 'You need to fill in all information'})
But that send the user to the wrong url, creating an error, as it adds the add_review to the to URL
http://127.0.0.1:8000/content/readerpage/41/add_review
Is there any way to pass along the error with the HttpResponseRedirect?
Or is there another alternative?
Thanks for reading this
I'm trying to create the functionality so that a user is returned to the url with an error message if they don't fill in the forms.
This is one of the main reasons to use Django for, so you should not try to re-implement the wheel at this point.
Use Django's generic views for your models, they will setup form validation and redirection as is convention, and just like you have described in your request.
https://docs.djangoproject.com/en/2.2/ref/class-based-views/generic-editing/
Saves you lots of code, tests, and problems.

Django Redirect after Successful Login

I have a requirement that whenever I login or attempt to request a view that has a login_decorator then the next page be a page where I am required to ask the user to select a business entity (irreespective of the original view requested).
Let's say that the page is http://127.0.0.1:8999/APP/business_unit
To achieve this I configured the following in my settings.py
LOGIN_REDIRECT_URL='/APP/business_unit_selector'
Now when i try to access http://127.0.0.1:8999/APP/range_list
the page goes to http://127.0.0.1:8999/APP/login?next=/APP/range_list I was expecting that the next page after login be /APP/business_unit
but instead, the next page was /APP/range_list
The browser address bar has http://127.0.0.1:8999/APP/login?next=/APP/range_list
Is it possible to achieve what I am trying in Django?
LOGIN_REDIRECT_URL is used unly when next is unspecified. In your test request there is next=/APP/range_list - and that address is used to redirect user after login.
Probably the easiest and most effective solution is to make your own decorator, similar to login_required which redirects to /APP/business_unit_selector&next=<redirect_url> if unit is not selected, and apply it together with login_required. It is not the most efficient solution in terms of redirects number, but is quite clean, and doesn't mess up the login page.
You will also have to handle next parameter in your business_unit_selector view, if you like to achieve natural flow.
Your decorator should be something like
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.http import urlquote
import functools
def business_unit_required(view):
#login_required # probably you want to incorporate it here
#functools.wraps(view)
def wrapped(request, *args, **kwargs):
if not 'selected_business_unit' in request.session:
return redirect('%s?next=%s' % (
reverse('business_unit_selector'),
urlquote(request.get_full_path())))
return view(request, *args, **kwargs)
return wrapped
The reason that http://127.0.0.1:8999/APP/login?next=/APP/range_list is redirecting you to range_list after logging in, is because with next= you are overriding what is specified in your settings file, LOGIN_REDIRECT_URL='/APP/business_unit_selector'.
If I understand correctly you need to user to choose a business entity after logging in.
A couple solutions that come to mind are as follows:
1.) Don't use a separate forms for login and business entity. Instead combine them.
Username
Password
Business Entity
2.) You can also specify in your view if the user doesn't have a buisness entity ResponseRedirect("/APP/business_unit_selector")
docs here

django HttpResponseRedirect and ABSOLUTE_URL_OVERRIDES

I'm new to django (1.5.1) and got a bit stuk with HttpResponseRedirect.
If I understand right that needs a static string, and when you want it to dynamic redirect, you get a reverse() or a get_absolute_url()
I think I get the part of the get_absolute_url, but I'm stuck on redirecting to the following url (first is mysite.urls.py second is my characters.view.py
url(r'^users/', include('characters.urls')),
url(r'^(?P<user_id>\d+)/characters/$', views.user_characters, name='user_characters'),
from this view:
if new_char_form.is_valid():
#save form with commit=False
new_char_obj = new_char_form.save(commit=False)
#set user and save
new_char_obj.user = user
new_char_obj.save()
return HttpResponseRedirect('%s/characters/' % user.id)
So I know I can't use the HttpResponseRedirect that way, and since I can't include a get_absolute_url function in the user model, I found the next option.
Include this in my settings
ABSOLUTE_URL_OVERRIDES = {
'auth.users': lambda o: "/users/%s/" % o.id,
}
but then I have no clue how to call for that.
Can someone please give me help (sort of stuck on this for some time)
With kind regards
Hans
The easiest way to redirect to the user_characters view is to use the redirect shortcut.
from django.shortcuts import redirect
# in the view
return redirect('user_characters', user.id)
This is equivalent to using HttpResponseRedirect and reverse
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
# in the view
return HttpResponseRedirect(reverse('user_characters', args=[user.id]))
If you want to use ABSOLUTE_URL_OVERRIDES to override the get_absolute_url method for the User model, you have to make sure that the url matches the format of your view named user_characters.
ABSOLUTE_URL_OVERRIDES = {
'auth.users': lambda o: "/users/%s/characters/" % o.id,
}
You would then be able to call user.get_absolute_url() in your views, and do
return HttpResponseRedirect(user.get_absolute_url())
Or, since the redirect shortcut allows you to pass a model instance:
return redirect(user)

django multiple admin instances and locking down access to a particular instance

I have several admin instances running on a site - one for each country, that the site supports.
However, if a user logs into one admin, they are automatically able to access other instances.
I need to make the auth code aware of which admin the user has logged into and prevent access to other admin systems.
Any ideas how this can be done?
You can use middleware to check for user permissions to access certain areas of admin site. Checkout this snippet. (You might want to know more about handling custom permissions in Django.)
If you need something more universal, you can use the code example below. The idea is simple: it uses custom functions to find out about user permissions and to give an appropriate response:
#coding: utf-8
# Note that RESTRICTED_URLS tuple takes three parameters: url regex, function to check
# whether user has certain permission, and a function to redirect the user to a certain
# page if he doesn't have sufficient rights.
import re
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponseRedirect
from django.contrib import messages
from backend.models import Professional
from django.contrib.auth.decorators import permission_required
def calculate_forbidden_response(request, view_func,view_args,view_kwargs):
if not request.user.is_authenticated():
return permission_required('')(view_func)(request,*view_args,**view_kwargs)
elif request.user.has_perm('backend.p_add_professional'):
messages.error(request, _('You need permission Spam to enter this cabinet.'))
return HttpResponseRedirect('/some_help_page_about_permissions.html')
def check_professional_permission(request):
return request.user.has_perm('backend.p_access_professional_cabinet')
RESTRICTED_URLS = (
(r'/professional/(.*)$', check_professional_permission, calculate_forbidden_response),
)
RESTRICTED_URLS_EXCEPTIONS = ()
class CheckPermissionMiddleware(object):
def __init__(self):
self.restricted = tuple([(re.compile(url[0]), url[1], url[2]) for url in RESTRICTED_URLS])
self.exceptions = tuple([re.compile(url) for url in RESTRICTED_URLS_EXCEPTIONS])
def process_view(self,request,view_func,view_args,view_kwargs):
if request.user.is_superuser:
return None
for path in self.exceptions:
if path.match(request.path): return None
for rule in self.restricted:
url, permission = rule[0], rule[1]
calculated_response = rule[2]
if url.match(request.path):
if not permission(request):
return calculated_response(request, view_func,view_args,view_kwargs)
else:
return None
return None

For a django model, how can I get the django admin URL to add another, or list objects, etc.?

As much as I love the django documentation, the section on bookmarklets in the admin is strangely vague.
My question is this: If I'm in a view and I have a django model (or, in some cases, an actual object), how can I get to the relevant admin pages for that model (or object)? If I have the object coconut_transportation.swallow.objects.all()[34], how can I jump right to the admin page to edit that particular swallow?
Likewise, how can I get the URL for the admin page to add another swallow?
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#reversing-admin-urls
obj = coconut_transportation.swallow.objects.all()[34]
# list url
url = reverse("admin:coconut_transportation_swallow_changelist")
# change url
url = reverse("admin:coconut_transportation_swallow_change", args=[obj.id])
# add url
url = reverse("admin:coconut_transportation_swallow_add")
You can retrieve this from the actual object instance, this worked for me:
from django.core import urlresolvers
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(object.__class__)
object_admin_url = django.core.urlresolvers.reverse("admin:%s_%s_change" % (content_type.app_label, content_type.model), args=(object.pk,))
See this: http://djangosnippets.org/snippets/1916/
You can actually retrieve the info without making a query to ContentTypes
Just add this to your model class:
def get_admin_url(self):
return urlresolvers.reverse("admin:%s_%s_change" %
(self._meta.app_label, self._meta.model_name), args=(self.pk,))