Django - Retrieve Unauthenticated User object - django

As the titles states, I have a need for users to retrieve User information of different users, whether or not they're authenticated.
I have a 'profile' template with some logic to either display static user information (for public) or editable user information (for the user it belongs to).
PROBLEM: The condition in my template is returning "true" - the User object appears to be authenticated BUT it's not. If I navigate from that page (profile) no user logged in.
I'm fairly new to Django. Am I not retrieving the User object correctly in my view.py?
views.py
def GetProfileById(request, userid):
try:
user = User.objects.get(pk=userid)
except User.DoesNotExist:
return HttpResponseRedirect('/')
try:
user_profile = user.get_profile()
except Exception:
return HttpResponseRedirect('/')
context = {'user': user, 'user_profile': user_profile}
return render_to_response('profile.html', context, context_instance=RequestContext(request))
profile.html
{% if user.is_authenticated %}
<form id="form-about" class="form-horizontal" action="{% url update-user-profile %}" method="POST">
{% csrf_token %}
<p>{{ about_form.first_name }}</p>
<p>{{ about_form.last_name }}</p>
<p>{{ about_form.birthday }}</p>
<p>{{ about_form.profession }}</p>
<button class="btn btn-primary" type="submit">Save</button>
</form>
{% else %}
<p><b>Name:</b> {{ user.first_name }} {{ user.last_name }}</p>
<p><b>DOB:</b> {{ user_profile.birthday }}</p>
<p><b>Profession:</b> {{ user_profile.profession }}</p>
<p><b>Member since:</b> {{ user.date_joined|date:'Y-M-d' }}</p>
<p><b>Last login:</b> {{ user.last_login|date:'Y-M-d # H:i' }}</p>
{% endif %}

It seems like, what you're trying to do is, if a user is currently logged in, you want to show a form that lets them update a profile.
So the problem is:
In your view, you're setting user to a User object from the database.
This object does not represent or contain information about whether the current user is logged in. It's just the record from the database.
You want to check request.user, which represents the User object for the user who requested the web page.
So in your template, change:
{% if user.is_authenticated %}
to:
{% if request.user.is_authenticated %}

I believe you're confusing user and request.user. The former is the user being displayed, the latter is the user that is currently logged in, or an AnonymousUser if no one is logged in.
You want to display that form if the user that is currently logged in and the user being displayed are the same, right? Then just change
{% if user.is_authenticated %}
To
{% ifequal user request.user %}

Related

Django custom admin actions with an intermediate page submit not triggered

Writing an admin action so an administrator can select a template they can use to send a message to subscribers by inputting only the subject and text message. Using a filtered list from the admin panel an action called broadcast is triggered on this queryset (the default filter list). The admin action 'broadcast' is a function of a sub-classed UserAdmin class. The intermediate page is displayed that shows a dropdown selector for the emailtype, the queryset items (which will be email addresses, input fields for the subject and message text (message is required field) a button for optional file attachment followed by send or cancel buttons. Problem 1) after hitting the send button the app reverts to the admin change list page. In the broadcast function, the conditional if 'send' in request.POST: is never called.
forms.py
mail_types=(('1','Newsletter Link'),('2','Update Alert'))
class SendEmailForm(forms.Form):
_selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
#Initialized 'accounts' from Account:admin.py Actions: 'send_email' using>> form = SendEmailForm(initial={'accounts': queryset})
my_mail_type=forms.ChoiceField(label='Mail Type',choices=mail_types,required=False)
subject = forms.CharField(widget=forms.TextInput(attrs={'placeholder': ('Subject')}),required=False)
message = forms.CharField(widget=forms.Textarea(attrs={'placeholder': ('Teaser')}),required=True,min_length=5,max_length=1000)
attachment = forms.FileField(widget=forms.ClearableFileInput(),required=False)
accounts = forms.ModelChoiceField(label="To:",
queryset=Account.objects.all(),
widget=forms.SelectMultiple(attrs={'placeholder': ('user_email#somewhere.com')}),
empty_label='user_email#somewhere.com',
required=False,
admin.py
from .forms import SendEmailForm
from django.http import HttpResponseRedirect,HttpResponse
from django.shortcuts import render, redirect
from django.template.response import TemplateResponse
def broadcast(self, request, queryset):
form=None
if 'send' in request.POST:
print('DEBUGGING: send found in post request')
form = SendEmailForm(request.POST, request.FILES,initial={'accounts': queryset,})
if form.is_valid():
#do email sending stuff here
print('DEBUGGING form.valid ====>>> BROADCASTING TO:',queryset)
#num_sent=send_mail('test subject2', 'test message2','From Team',['dummy#hotmail.com'],fail_silently=False, html_message='email_simple_nb_template.html',)
self.message_user(request, "Broadcasting of %s messages has been started" % len(queryset))
print('DEBUGGING: returning to success page')
return HttpResponseRedirect(request, 'success.html', {})
if not form:
# intermediate page right here
print('DEBUGGING: broadcast ELSE called')
form = SendEmailForm(request.POST, request.FILES, initial={'accounts': queryset,})
return TemplateResponse(request, "send_email.html",context={'accounts': queryset, 'form': form},)
send_email.html
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST" enctype="multipart/form-data" action="" >
{% csrf_token %}
<div>
<div>
<p>{{ form.my_mail_type.label_tag }}</p>
<p>{{ form.my_mail_type }}</p>
</div>
<div>
<p>{{ form.accounts.label_tag }}</p>
<p>
{% for account in form.accounts.queryset %}
{{ account.email }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
<p><select name="accounts" multiple style="display: form.accounts.email">
{% for account in form.accounts.initial %}
<option value="{{ account.email }}" selected>{{ account }}</option>
{% endfor %}
</p></select>
</div>
<div>
<p>{{ form.subject.label_tag }}</p>
<p>{{ form.subject }}</p>
</div>
<div>
<p>{{ form.message.label_tag }}</p>
<p>{{ form.message }}</p>
</div>
<div>
<p>{{ form.attachment.label_tag }}</p>
<p>{{ form.attachment.errors }}</p>
<p>{{ form.attachment }}</p>
</div>
<input type="hidden" name="action" value="send_email" />
<input type="submit" name="send" id="send" value="{% trans 'Send messages' %}"/>
{% trans "Cancel this Message" %}
</div>
</form>
{% endblock %}
Inspecting the browser at the POST call seems to show all the data was bound. Another poster here suggested the admin action buttons divert requests to an internal 'view' and you should redirect to a new view to handle the POST request. I can't get that to work because I can't get a redirect to 'forward' the queryset. The form used in the suggested fix was simpler and did not use the queryset the same way. I have tried writing some FBVs in Forms.py and Views.py and also tried CBVs in views.py but had issues having a required field (message) causing non-field errors and resulting in an invalid form. I tried overriding these by writing def \_clean_form(self): that would ignore this error, which did what it was told to do but resulted in the form essentially being bound and validated without any inputs so the intermediate page didn't appear. Which means the rabbit hole returned to the same place. The send button gets ignored in either case of FBVs or CBVs, which comes back to the admin action buttons Post requests revert to the admin channels!
Any ideas on how to work around this? Key requirements: From the admin changelist action buttons:
the Form on an intermediate page must appear with the queryset passed from the admin changelist filter.
The message input field on the form is a required field.
the send button on the HTML form view needs to trigger further action.
NOTES: My custom Admin User is a subclass of AbstractBaseUser called Account, where I chose not to have a username and am using USERNAME_FIELD='email'. Also, I do not need a Model.py for the SendEmailForm as I don't need to save the data or update the user models, just send the input message using the chosen template and queryset. Help is much appreciated!
It will never work in your case:
You call the action.
You receive the Action Confirmation template render.
After pressing "SEND" in your "confirmation" step, you send a POST request to ModelAdmin, not in your FB-Action.
ModelAdmin gets a POST request without special parameters and shows you a list_view by default.
In your case, you should add a send_email.html template:
{% load l10n %}
{# any your staff here #}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{# any your staff here #}
<div>
<p>{{ form.attachment.label_tag }}</p>
<p>{{ form.attachment.errors }}</p>
<p>{{ form.attachment }}</p>
</div>
{% for obj in accounts %}
<input type="hidden" name="_selected_action" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="action" value="broadcast" />
{# any your staff here #}
</form>
{% endblock %}
You should change your action view, some things are not working in your code:
def broadcast(self, request, queryset):
form = SendEmailForm(data=request.POST, files=request.FILES, initial={'accounts': queryset})
if 'send' in request.POST:
... # your staff here
if form.is_valid():
... # your staff here
# return HttpResponseRedirect(request, 'success.html', {} ) this is NEVER WORK
return TemplateResponse(request, 'success.html', {})
... # your staff here
return TemplateResponse(request, "send_email.html",context={'accounts': queryset, 'form': form},)
I am giving you a solution that I have TESTED on my project. I am sure, it works.
We were told on DjangoCon Europe 2022 that django-GCBV is like a ModelAdminAction and I've added a link below for the talk.
https://youtu.be/HJfPkbzcCJQ?t=1739
I can't get that to work because I can't get a redirect to 'forward' the queryset
I have a similar use case and save the primary keys of the filtered query set in the session (in your case you may be able to save emails and avoid another query)
def broadcast(self, request, queryset):
request.session["emails"] = list(queryset.values_list("emails", flat=True))
return HttpResponseRedirect("url_to_new_view")
I can then use primary keys to filter query set in the new view. You also handle the form in this new view.
User.objects.filter(email__in=self.request.session["emails"])

why django username parameters passed to urls is connecting directly and how prevent it

I'm trying to access to a user profil if urls parameters is profile/username
it work but i have a security problem because user is directly connected.
How i can define in django user profil access depending if user is authenticated or not
profil.html
{% if user.is_authenticated %}
<p>{{ user.username}}</p>
edit your profile
{% else %}
<p>{{ user.username}}</p>
<p>basic profile of user</p>
{% endif%}
views.py
def profil(request,username):
user=get_object_or_404(User, username=username)
context = {
'user':user
}
return render(request, 'service/profil.html',context)
You can use the login required decorator. Just add it to your view:
from django.contrib.auth.decorators import login_required
#login_required
def profil(request, username):
...
I fixed my problem condition was
{% if request.user == requested_user %}
<p>{{ requested_user.username}}</p>
edit your profile
{% else %}
<p>{{ requested_user.username}}</p>
<p>basic profile of user</p>
{% endif%}

Can't get user in template using django.contrib.auth

What am i doing wrong? I just want the username of the user. I am logged so i don't know why it's not working.
views.py
from django.contrib.auth.models import User
def check_user(request):
data = dict()
user = User.objects.all()
data['user'] = user
return render(request, 'check_user.html', data)
urls.py
url(r'^check_user/$', views.check_user, name='check_user'),
check_user.html
{{ request.user.is_authenticated }}
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
And i'm getting this:
CallableBool(True)
Welcome, new user. Please log in.
when i should get:
Welcome, admin. Thanks for logging in.
If i use: {% if request.user.is_authenticated %} instead of {% if user.is_authenticated %} i'm getting this:
Welcome, . Thanks for logging in.
Your problem is in your view, you don't need to create data['user']. Because in template, Django already pass the template variable {{ user }}.
So in your view remove this data dict, and keep the current context, without modification.
def check_user(request):
return render(request, 'check_user.html', {})
And your template will work. (don't need to change something)
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
https://docs.djangoproject.com/en/dev/topics/auth/default/#authentication-data-in-templates
user is queryset object in your template, in other words it is list of users, not single user.
If you need current user use this:
{% if request.user.is_authenticated %}
<p>Welcome, {{ request.user.username }}. Thanks for logging in.</p>
instead of
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
UPDATE
As Wilfried said in his answer and comment, it is not good practice to override variable user in template. So if you need list of users in view it would be better to rename context variable to users:
users = User.objects.all()
data['users'] = users

How to display other user's profile

Please, I am creating a web app, when a user log in, he/she can see other users, and can click on the user's username to view that user's profile. But I am finding it difficult to display other users profile except the currently logged in user. Kindly help me on how to go about this. I would prefer a function based view.
views
def profile(request, username):
context = {
'userprofile': User.objects.get(username=username),
}
return render_to_response('profile.html',context)
profile.html
Name: {{ userprofile.get_full_name }}
Username: {{ userprofile.username }}
urls
url(r'^/profile/(?P<username>\w+)/$', auth(profile), {}, name='chat_history')
what I don't know is how apply this url on a username. As in this:
home.html
{% if messages %}
{% for message in messages %}
{% if message.sender == user %}
<p> {{ message.sender }} >
{{ message.receiver }} : <br/>
{{ message.message }}
<sub>{{ message.creation_date }}</sub>
</p>
{% endif %}
{% endfor %}
{% else %}
{% trans 'No chat history of you and this user' %}
{% endif %}
Have got no idea what 'xxxxx' should be
Probably importing User model from django.contrib.auth and passing it to your views could help.

Django URL user id versus userprofile id problem

I have a mini comunity where each user can search and find another user's profile.
Userprofile is a class model, indexed differently compared to user model class (user id is not equal to userprofile id).
But I cannot see a user profile by typing in the URL the corresponding id. I only see the profile of the currently logged in user.
Why is that?
I'd also want to have in my URL the username (a primary key of the user table also) and NOT the id (a number).
The guilty part of the code is:
What can I replace that request.user with so that it will actually display the user I searched for, and not the currently logged in?
def profile_view(request, id):
u = UserProfile.objects.get(pk=id)
cv = UserProfile.objects.filter(created_by = request.user)
blog = New.objects.filter(created_by = request.user)
return render_to_response('profile/publicProfile.html',
{
'u':u,
'cv':cv,
'blog':blog,
},
context_instance=RequestContext(request))
In file urls.py (of the accounts app):
url(r'^profile_view/(?P<id>\d+)/$',
profile_view,
name='profile_view'),
And in template:
<h3>Recent Entries:</h3>
{% load pagination_tags %}
{% autopaginate list 10 %}
{% paginate %}
{% for object in list %}
<li>{{ object.post }} <br />
Voted: {{ vote.count }} times.<br />
{% for reply in object.reply_set.all %}
{{ reply.reply }} <br />
{% endfor %}
<a href=''> {{ object.created_by }}</a> <br />
{{object.date}} <br />
Vote this
Comment </li>
{% endfor %}
Replace
cv = UserProfile.objects.filter(created_by = request.user)
blog = New.objects.filter(created_by = request.user)
With
#u is UserProfile.objects.get(pk=id)
cv = UserProfile.objects.filter(created_by = u)
blog = New.objects.filter(created_by = u)