Username in URL for Django User Profile - regex

I have a Django project that uses profiles for user information. Things are somewhat working except for one aspect... Here are code snippets to describe my problem.
In the template:
<li>Profile</li>
In views.py
class UserProfileView(View):
#method_decorator(login_required)
def get(self, request, user):
profile = get_object_or_404(UserProfile, user=request.user)
return render(request, 'accounts/profile.html', {'profile': profile})
In urls.py
url(r'^accounts/(?P<user>.+)/profile/$',
UserProfileView.as_view(),
name='user_profile_view'
),
I've tried variations for the named group, and this is what I found to work. The problem is, I can use any string in between /accounts/ and /profile/ (obviously) and it works. What I want to accomplish is to have only the current user's username be valid in the URL and otherwise throw a 404.

Do you really need the user parameter in the profile URL? If you only want it work for the current user, then why not simply drop the user parameter:
# urls.py
url(r'^accounts/profile/$',
UserProfileView.as_view(),
name='user_profile_view'
),
# views
class UserProfileView(View):
#method_decorator(login_required)
def get(self, request):
profile = get_object_or_404(UserProfile, user=request.user)
return render(request, 'accounts/profile.html', {'profile': profile})
In the code you posted, the UserProfileView.get method was not using the user parameter anyway.
UPDATE
If you want to keep the user parameter and make it work the way you want, you can change the view like this:
from django.http import Http404
class UserProfileView(View):
#method_decorator(login_required)
def get(self, request, user):
if request.user.username == user:
profile = get_object_or_404(UserProfile, user=request.user)
return render(request, 'accounts/profile.html', {'profile': profile})
else:
raise Http404
Btw, since user in the parameter list of the get method is really just the username as opposed to a user object, it would be better to rename it to username, to avoid confusion.

Related

Why django PermissionRequiredMixin is not working as it intended to be

Here i have got a Django view that should normally redirect me to the Settings.LOGIN_URL if user has not the required permission but the issue is it's always displaying the 403 FORBIDDEN page.
the url defined in Settings.LOGIN_URL is 'accounts/login/'.
class LibrairianListView(PermissionRequiredMixin, View):
model = BookInstance
template_name = 'catalog/librairian_list_borrowed.html'
permission_required = 'catalog.can_mark_returned'
def get_queryset(self):
return BookInstance.objects.filter(status__exact='o').order_by('due_back')
def get_context_data(self, **kwargs):
print(self.raise_exception)
context = {}
context['bookinstance_list'] = self.get_queryset()
return context
def get(self, request, *args, **kwargs):
return render(request, self.template_name, self.get_context_data())
And i change this to a function based view and everything is working fine, but i wanted to use class base view since this should do the same work
This happened because the user is logged-in but he does not have 'catalog.can_mark_returned' permission. This is indented behavior and no need to redirect the user to login page since he is already logged-in.
If you still wish to modify the way the permission error handled, override the handle_no_permission()--(Django doc) method

python - Django redirect to next after login (Generic Form View)

I am using generic form view for authentication, I am getting next parameter in url but unfortunately I don't know how to redirect it to next, after successful login for Generic Form View, here is my view
class LoginView(
views.AnonymousRequiredMixin,
generic.FormView):
form_class = LoginForm
success_url = reverse_lazy('home')
template_name = 'accounts/registered/login.html'
def form_valid(self, form):
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
if user is not None and user.is_active and user.is_seller:
login(self.request, user)
return super(LoginView, self).form_valid(form)
else:
return self.form_invalid(form)
I am getting this
http://127.0.0.1:8000/accounts/login/?next=/accounts/dashboard/
help me out!
So essentially, what the url that you are getting means is that it's trying to go to 127.0.0.1:8000/accounts/dashboard/, but because the user needs to be logged in, it's going to the login page first. Essentially, this means that your view is not logging the user in for some reason.
Try using (or extending) Django's built in LoginForm class (https://docs.djangoproject.com/en/2.0/topics/auth/default/#django.contrib.auth.views.LoginView)
Alternatively, go with a broader solution suite, such as django allauth (https://github.com/pennersr/django-allauth/blob/master/docs/index.rst)
You should use HttpRedirectResponse:
views.py
from django.http import HttpResponseRedirect
def login(request):
# You logic goes here
return HttpResponseRedirect('dashboard')
def dashboard(request):
context = {
# User information goes here
}
return render(request, 'dashboard', context)
Do not forget to add this call to the login method in your urls.py:
path('login', views.login, name='login'),
path('dashboard', views.dashboard, name='dashboard'),
You should also take a look at https://docs.djangoproject.com/en/2.0/ref/request-response/ for a better understanding of how request and response work.
You should also be familiar with https://docs.djangoproject.com/en/2.0/intro/tutorial04/ so that you could understand an example of HttpResponseRedirect.

Automatic HTTPRedirect in Django view if user is authenticated?

I have successfully made it so the user must log in to view their profile, however, I only want the user to be able to view their profile and no one else.
Previously, they could visitwww.websitename.com/user/admin
as well as www.websitename.com/user/test and it would bring up the data for the profile each time of the logged in user.
The URL to visit is www.websitename.com/user/usernameofcurrentuser
Profile Page View
def profile_page(request, username):
context = RequestContext(request)
if request.user == username:
if request.user.is_authenticated():
user = User.objects.get(username=username)
taskitems = request.user.taskitem_set.all()
return render_to_response('profile.html', {}, context)
else:
return render_to_response('login.html', {}, context)
else:
return render_to_response('login.html', {}, context)
However, even though the user is logged in, it's redirecting them to the sign in page. I know the user is logged in because it prints their name on the login page, yet it's not redirecting them to the profile associated with the username.
You can try this :
def profile_page(request, username):
if request.user.is_authenticated():
if request.user.username == username:
# removed user since already in request.user and available in template as 'user'
# removed taskitems since directly available in template as 'user.taskitem_set.all'
return render(request, 'profile.html')
else:
return HttpResponseRedirect(reverse('profile_page', args=(request.user.username,)))
else:
return render(request, 'login.html')
Remove the username parameter since each user should only view their own profile. You can also use the login_required decorator to remove the extra conditional:
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
#login_required
def profile_page(request):
return render(request, 'profile.html')
Make sure to set LOGIN_URL in your settings.py so unauthenticated users get redirected to the right spot.

Should I remove user id from URL if I want to keep data available just to exactly one user?

Lets just suppose that we have following url:
example.com/users/1
if user with ID=1 opens it, user receive info about his account, but if user switch 1 with 2, then he can view other user details as well, and we of course do not want that, so I have following solutions:
1) just pass currently logged in user id threw request.user.id inside a template
2) add permission, but I did not find type of permissions that would allow me to do that. Of course I could create dozens of permissions each for each user, but of course that is very nasty way.
Any other ideas how to cope with that in Django?
You can either fill context with the request which makes sure the user will never see another user's data, e.g. (using CBVs):
class AccountView(TemplateView):
"""
Generic account view
"""
template_name = "users/account.html"
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
context['user'] = User.objects.get(id=self.request.user.id)
return context
#method_decorator(login_required(login_url=reverse('login')))
def dispatch(self, *args, **kwargs):
return super(AccountView, self).dispatch(*args, **kwargs)
Another approach, to make sure 'fake' urls render 404's is to write an owner_required decorator, e.g.:
def owner_required(function):
#wraps(function)
def decorator(*args, **kwargs):
request = args[1]
user = get_object_or_404(User, username=request.user.username)
if user.is_authenticated() and user.username == kwargs.get('slug'):
return function(*args, **kwargs)
raise Http404
return decorator
You don't need permission to do this.
In your views.py
from django.http import Http404
def myview(request, user_id):
user = request.user
if user_id != request.user.id:
raise Http404
#all your logic here
But if you want user profile to be private, you don't need to use user_id in your url pattern. just use the user object stored in the request variable.

try/except in django, good practices

This part of code represents a index of my web page
eg: 127.0.0.1:8000/
def IndexView(request):
try:
profile = request.user.get_profile()
except User.DoesNotExist:
return render_to_response('index.html',
{'request': request,},
context_instance=RequestContext(request))
return render_to_response('index.html',
{'request': request, 'profile' : profile},
context_instance=RequestContext(request))
Why i'm still getting this error on debug?
AttributeError at /
'AnonymousUser' object has no attribute 'get_profile'
thanks in advance
You need to add this check:
from django.shortcuts import render
def index_view(request):
if request.user.is_authenticated():
profile = request.user.get_profile()
return render(request,'index.html',{'profile': profile})
else:
return redirect('login/')
Or, you can use the built-in decorator which ensures that your view is only called with a logged in user:
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
#login_required
def index_view(request):
profile = request.user.get_profile()
return render(request,'index.html',{'profile': profile})
If you use the login_required decorator you need to make sure you have a LOGIN_URL setting that points to the view that handles the login form for your site. By default this is /accounts/login/
I changed the method name to lowercase as CamelCase is usually for classes in Python. Also, you don't need two return statements since you are rendering the same template. Instead, in your index.html template you can do:
{% if profile %}
You have a profile!
{% else %}
You don't
{% endif %}
Tips and tricks with authentication are listed at the authentication entry in the documentation.
Because a user isn't currently logged in. When you call get_profile it gets called on a default user provided by Django called AnonymousUser which doesn't have a get_profile method as it isn't a real user. It doesn't throw an exception I am assuming and hence you get an error.
Try the same thing after logging in and it should be fine.
You are expecting a User.DoesNotExist error, but instead the code is throwing an AttributeError. This is because request.user is a real object -- an AnonymousUser object -- and accessing it does not cause an error, but an AnonymousUser has no profile.
Use the #login_required decorator instead, or catch the AttributeError, or adjust your code so that it actually does throw User.DoesNotExist.
You need to check if the user is authenticated before you try to pull the profile through get_profile. You can also simplify your code quite a bit:
def IndexView(request):
profile = request.user.is_authenticated() and request.user.get_profile()
return render_to_response('index.html',
{'request': request, 'profile': profile},
context_instance=RequestContext(request))
You don't need the except, because the User with always be there.
You are getting this error because this specific user is not logged in.
To prevent the error either use the #login_required decorator just over your view:
from django.contrib.auth.decorators import login_required
#login_required
def IndexView(request):
...
or:
def IndexView(request):
if request.user.is_authenticated(): #check to use if user is logged in
try:
profile = request.user.get_profile()
except User.DoesNotExist:
return render_to_response('index.html',
{'request': request,},
context_instance=RequestContext(request))
return render_to_response('index.html',
{'request': request, 'profile' : profile},
context_instance=RequestContext(request))
Just to let you know, that's not very dry code.
check it:
def index_view(request):
context_data = {'request': request}
try:
context_data.update('profile': request.user.get_profile())
except AttributeError:
pass
return render_to_response('index.html', context_data,
context_instance=RequestContext(request))
Also, I'd double check and make sure request isn't already included in context. You may not have to specifically put it into context here.
EDIT: Forgot to pass context_data to render_to_response, example updated.