How to do a DetailView of Users in Django? - python

I currently have a DetailView for Django's built-in User.
url(
r'^users/(?P<pk>\d+)/$',
DetailView.as_view(
model = User,
template_name = 'doors/users/detail.html'
),
name = 'users_detail'
)
But when I access user in the template, it brings up the current logged in user, not the user with the pk that I pass from DetailUser. Do I need to tell DetailUser to rename the user variable to something else? And if so, how do I do that?

The django.contrib.auth.context_processors.auth sets the {{ user }} template context variable to either request.user or AnonymousUser. So, it overrides the {{ user }} context variable created by your DetailView:
def auth(request):
"""
Returns context variables required by apps that use Django's authentication
system.
If there is no 'user' attribute in the request, uses AnonymousUser (from
django.contrib.auth).
"""
# If we access request.user, request.session is accessed, which results in
# 'Vary: Cookie' being sent in every request that uses this context
# processor, which can easily be every request on a site if
# TEMPLATE_CONTEXT_PROCESSORS has this context processor added. This kills
# the ability to cache. So, we carefully ensure these attributes are lazy.
# We don't use django.utils.functional.lazy() for User, because that
# requires knowing the class of the object we want to proxy, which could
# break with custom auth backends. LazyObject is a less complete but more
# flexible solution that is a good enough wrapper for 'User'.
def get_user():
if hasattr(request, 'user'):
return request.user
else:
from django.contrib.auth.models import AnonymousUser
return AnonymousUser()
return {
'user': SimpleLazyObject(get_user),
'messages': messages.get_messages(request),
'perms': lazy(lambda: PermWrapper(get_user()), PermWrapper)(),
}
You can work around the issue by setting context_object_name. For example, this will enable the {{ user_object }} context variable, set to the user of the DetailView:
url(
r'^users/(?P<pk>\d+)/$',
DetailView.as_view(
model = User,
template_name = 'doors/users/detail.html',
context_object_name = 'user_object'
),
name = 'users_detail'
)
Dig deeper, read the documentation for get_context_object_name().

Related

How to prevent users from seeing data that does not belong to them in Django DetailView?

I have a web app where a user signs in and begins entering items for a ToDoList. The base.html is wrapped in an is_authenticated check so users cannot see anything in the app until they have logged in. I was doing some testing with:
Logging in as User2
Adding a new ToDoListItem
Redirecting to the DetailView
In this case, the URL = http://localhost:8000/to_do_list/to_do_item/72
At this point I realized that the DetailView would allow User2 to see the details for any ToDoListItem for User1 just by entering in an existing pk into: http://localhost:8000/to_do_list/to_do_item/<int:pk>.
urls.py includes
path('to_do_item/<int:pk>', views.ToDoListItemDetail.as_view(), name='todo-item-detail'),
views.py
class ToDoListItemDetail(DetailView):
model = ToDoListItem
todolistitem_detail.html
{% extends 'base.html' %}
{% block content %}
Home
<h1>DetailView for 'ToDoListItem' model</h1>
<p>TaskTitle: '{{ object.title }}'</p>
<p>Complete: '{{ object.is_complete }}'</p>
<p>User: '{{ object.user}}'</p>
{% endblock %}
What is the recommended way to prevent this from happening? I am considering the following:
I could completely remove the DetailView and direct to a different URL that only returns the user's data (using something like ToDoListItem.objects.filter(user=request.user))
I could check that the name of the user logged in matches the name of the user that owns the ToDoListItem.
I could override get_context_data() for the DetailView and check user ownership there (similar to no. 1, but within the DetailView)
??? (Something else better than the above I do not know about yet)
Is there a way to limit a user to only see their own data throughout an application without implementing this logic every time it is needed?
You can filter in the DetailView as well, by overriding the get_queryset method [Django-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
class ToDoListItemDetail(LoginRequiredMixin, DetailView):
model = ToDoListItem
def get_queryset(self, *args, **kwargs):
return super(ToDoListItemDetail, self).get_queryset(
*args, **kwargs
).filter(user=self.request.user)
Django will, behind the curtains, always call get_queryset(..). By default this function returns the queryset of the model you specified with all objects. But you thus can filter it further down.
Django's get_object method [Django-doc] will then further filter it down with the id and/or slug, but if you already filter out the elements that do not belong to the self.request.user, then this can thus only result in an query returning no results.
It also makes sense to here add the LoginRequiredMixin [Django-doc] to your class, since in case the user did not log in, you probably want to redirect hem/her to the login screen.
There is a permission named: ¨can read¨ that allow you to handle the access. For example:
class FruitEdit(PermissionRequiredMixin,DetailView):
permission_required = 'app.read_fruit'
...
I hope you can solve it

Django Allauth Signup Prevent Login

Is there anyway to prevent the Allauth Signup form from automatically logging in the User?
I found a similar post here with no answers:
prevent user login after registration using django-allauth
You get logged in because this behavior is baked into the signup view of the allauth. When the form is valid, the view calls the function called complete_signup that does two things:
Emits the user_signed_up signal
Logs a user in
To solve this, we need to leave the step 1 and replace the step 2 with a simple redirect.
Here's how this can be done:
Extend the SignupView from allauth/account/views.py and override its form_valid method like this:
class CustomSignupView(SignupView):
def form_valid(self, form):
# By assigning the User to a property on the view, we allow subclasses
# of SignupView to access the newly created User instance
self.user = form.save(self.request)
try:
signals.user_signed_up.send(
sender=self.user.__class__,
request=self.request,
user=self.user,
**{}
)
return HttpResponseRedirect(self.get_success_url())
except ImmediateHttpResponse as e:
return e.response
Haven't tested the code, but it should be working.
Wire your new view up in urls.py, so it replaces the old Signup view url.
I am using django-allauth 0.42.0
There is no need to extend the SignUp view, it can be achieved by the setting:
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
in your project settings.py file.

How to render an html template with data from view?

I am new to django. I made a form. I want that if the form is filled successfully then django should redirect to a success page showing the name entered in the form but no parameters should be present in the url itself.
I searched on the internet and the solution I got was to redirect to url with pk as a get parameter which fetches the data and shows in the view. But I don't want to pass any thing in the url itself. and some websites say that http can't redirect with post data.
Here's my views.py
class UserRegistrationView(CreateView):
model = UserForm
template_name = 'userregistration.html'
form_class = UserForm
success_url = 'success'
def get_success_url(self):
return reverse('success',kwargs = {'name' : self.object.firstName})
and here's the template to which I want to redirect:
<h2>Congratualations for registering {{name}} </h2>
Basically what I want is that if the person fill form mentioning his/her firstName as "xyz" then the redirected success page should say that "Congratulations for registering xyz"
You can use django sessions, which I believe installed by default in 1.8
Look here
# Set a session value:
request.session["fav_color"] = "blue"
# Get a session value -- this could be called in a different view,
# or many requests later (or both):
fav_color = request.session["fav_color"]
# Clear an item from the session:
del request.session["fav_color"]
You can pass your pk via session and extract your object in another view without affecting your url.
Make sure you clean up after yourself.
Let me know if more help needed.
One of the possible ways of passing data between views is via sessions. So, in your UserRegistrationView you need to override the form_valid method as shown below.
class UserRegsitrationView(CreateView):
def form_valid(self,form):
self.request.session['name'] = self.object.firstName
return super(UserRegistrationView,self).form_valid(form)
class SuccessView(TemplateView):
template_name = "success_template.html"
def get_context_data(self,**kwargs):
context = super(SuccessView,self).get_context_data(**kwargs)
context['name'] = self.request.session.get('name')
del self.request.session['name']
return context
One more thing that you can modify in your code is that you need not declare success_url if you are overriding get_success_url

Add to the django request.user variable

In django, I use the authenticate() function to log users in. Using request.user, I can access the logged in user's information which is pulled from the users table. If I do request.user.id, I can get the user's id. In any template, on any request, I can do user.username to get the username.
Is there a way by which I can add values to the request.user variable so that I can use them throughout my application and in templates?
For example, I would like to have this: request.user.timezone set when the user logs in to the site; I should be able to access this variable in templates and in my views.
You can write a simple middleware:
class UserTZMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
if request.user.is_authenticated():
request.user.timezone = evaluate_tz()
and then append this into MIDDLEWARE_CLASSES in your settings.py file:
MIDDLEWARE_CLASSES = (
....,
'myapp.UserTZMiddleware',
)
Would you like to let the user change it time-zone and force a specific time-zone ? I assume you would, if so you would like to keep the timezone configurable in your database. Therfore I would suggest you to make a UserProfile model which has one to one relations with the User Object.
You can do :
class UserProfile(models.Model):
user = models.OneToOneField(User)
time_zone = models.whateverField()
... add anything else you would like to extend your profile ...
On your templete you will be able to get the timezone by doing that :
{{ request.user.userprofile.time_zone }}
Simple and very clean.
If you would like to have an automatic creation of your userprofile whenever you save a User object, you can use signals for that.
Add the request context processor to your TEMPLATE_CONTEXT_PROCESSORS. This processor adds the request to the context, in your template you can do {{request}} or {{ request.user }}
see: https://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request
Use sessions, which are designed to keep variables in the context.
You should not change request.user as this is something that other applications will rely on and one that is populated by django's own auth machinery.
If you need to keep track of the timezone specifically, you should read up on the timezone support in django 1.4. In the documentation it provides examples for setting and managing timezones for users.

Including user statistics for multiple views

I have a navigation bar that displays "login" and "register" when the user is not signed in. When the user is signed in, the navigation bar will display the user's name and the number of messages he has in his inbox.
The problem is that the navigation page is present on around 50 pages, so there are around 50 view functions that have to get the user information and send it to the template. If I want to change this later, it will be a pain!
For example, here is an example view:
def index(request):
user = request.user
...
return render_to_response("page.html", {'user': user})
I have to send the info about the user each time to any page with the navigation bar because my navigation bar contains the code:
{% if user %}
...
{% else %}
....
{% endif %}
Is there a cleaner way to do this?
Edit: Also, I have a UserProfile model which I want to send through to the template. Is there a way to do this, too?
The simplest way is to include django.contrib.auth.context_processors.auth to the TEMPLATE_CONTEXT_PROCESSORS configuration in your settings.py. As described in the docs it wil add a user and perms variable in your template context which gives you direct access to the current user.
Not that the default configuration for TEMPLATE_CONTEXT_PROCESSORS is this (in Django 1.3):
("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages")
So the context processor should already be active und you should be able to access the user variable in you templates without returning it in the view.
In your views, you can simply use the render shortcut which will take care of creating the RequestContext instance that is needed:
from django.shortcuts import render
def my_view(request):
return render(request, 'template.html' )
You could create a custom template tag, as DrTyrsa suggested, or you could create context processor.
You need a custom template tag for that.
Why you need to send user to every view, HttpRequest contains the user, you can easily access it in template when you are logged in.
and another solution is, save the user in sessions, and access it in any view or template.

Categories

Resources