Django request.user not showing correct user in frontend - python

When user updates profile with username which is already in use ,message appear of "A user with that username already exists." but username on profile is changed to which user requested.
for example: if user with username test001 update profile with already taken username "test0012"
this will happen:
Notice:username is updated with "test0012"
Code in frontend for username:
<h2 id="user-name" class="account-heading user__name">#{{ user.username }}</h2>
also going on "my posts" will redirect to posts of user "test0012".
code for profile and update:
#login_required
def profile(request):
if request.method=="POST":
u_form=UserUpdateForm(request.POST,instance=request.user)#,op_email=request.user.email)
p_form=ProfileUpdateForm(request.POST,request.FILES,instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request,f'Your Account has been updated!')
#changing email on profile
tusername=u_form.cleaned_data.get('username')
temail=u_form.cleaned_data.get('email')
registeruser=User.objects.get(username=tusername)
registeruser.email=temail
registeruser.save()
return redirect('profile')
else:
u_form=UserUpdateForm(instance=request.user)
p_form=ProfileUpdateForm(instance=request.user.profile)
userupdateform code:
class UserUpdateForm(forms.ModelForm):
email=forms.EmailField()
username=forms.CharField(required=True,validators=[username_check,])
class Meta:
model =User
fields =['username','email']
ProfileUpdateForm:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model=Profile
fields=['image']
username_check:
def username_check(someusername):
if someusername!=someusername.lower():
raise forms.ValidationError("Only Lower case allowed.")

The model form first does it's own cleaning / validation, after which it assigns this data to the instance that you passed to it and calls that instance's full_clean method which is what gives you the error A user with that username already exists. But as you notice this is already too late as the instance is already modified, although this does not get saved to the database you are displaying the same instance.
The solution to this is to get a copy of the user instance from the database just to pass it to the form, so that the instance associated with the request stays unchanged and clean:
from django.contrib.auth import get_user_model
UserModel = get_user_model()
#login_required
def profile(request):
user = UserModel.objects.get(pk=request.user.pk)
if request.method == "POST":
u_form = UserUpdateForm(request.POST, instance=user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=user.profile)
...
else:
u_form = UserUpdateForm(instance=user)
p_form = ProfileUpdateForm(instance=user.profile)

Related

Django 3.2 Model not instantiating properly in the register function

I apologize if my mistake is incredibly simple but I am completely new to Django. Currently, my models.py currently contains 2 types of profiles extending off of the default Django User model:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
first_name = models.CharField(max_length = 50)
last_name = models.CharField(max_length = 50)
def __str__(self):
return self.user.username
class PCOProfile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
org_name = models.CharField(max_length = 100)
org_phone = models.CharField(max_length = 20)
is_active = models.BooleanField(default = False)
def __str__(self):
return f"{self.user.username} | {self.org_name}"
I have a views.py file which contains 2 functions, one called register and another register-pco that collects base user information, as well as information related to only one of of these models as a form on the same page which will be submitted at the same time as the base user information:
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegistrationForm, UserProfileForm, PCOProfileForm
def register(request):
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
profile_form = UserProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
# Hooking up user_profile model to django default user model
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
username = user_form.cleaned_data.get('username')
messages.success(request, f'Your account has been created. You can now login as {username}!')
return redirect('login')
else:
user_form = UserRegistrationForm()
profile_form = UserProfileForm()
context = {'user_form':user_form, 'profile_form':profile_form, 'title':'Register'}
return render(request, 'users/register.html', context)
def register_pco(request):
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
pco_profile_form = PCOProfileForm(request.POST)
if user_form.is_valid() and pco_profile_form.is_valid():
user = user_form.save()
# Hooking up pco model to django default user model
pco_profile = PCOProfileForm.save(commit=False)
pco_profile.user = user
pco_profile.save()
username = user_form.cleaned_data.get('username')
messages.success(request, f'Your account has been created. You can now login as {username}!')
return redirect('login')
else:
user_form = UserRegistrationForm()
pco_profile_form = PCOProfileForm()
context = {'user_form':user_form, 'pco_profile_form':pco_profile_form, 'title':'Register'}
return render(request, 'users/register.html', context)
#login_required
def profile(request):
return render(request, 'users/profile.html')
The problem I am currently experiencing is that when I submit the registration form on the register-pco path, it fails to instantiate a PCOprofile object while the base user information gets saved in the database just fine. The end result is a TypeError at /register-pco/ save() missing 1 required positional argument: 'self'. Additionally, the regular register path works just as expected, adding a user in the database with a corresponding profile.
For reference, I was following this guide to try and get user types setup in a simple manner (via 1-1 fields) but it seems as if there might be something wrong with the way I am making the forms since the models themselves work (I tested them in the django shell).
Here is the forms.py file without the imports:
class UserRegistrationForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username','email','password1','password2']
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
fields = ['first_name','last_name']
class PCOProfileForm(ModelForm):
class Meta:
model = PCOProfile
fields = ['org_name','org_phone']
You're not calling save() on the instance of form you created, but on the form class. So instead of this:
# Hooking up pco model to django default user model
pco_profile = PCOProfileForm.save(commit=False)
you should call it with the form instance you assigned into variable pco_profile_form a few lines above:
# Hooking up pco model to django default user model
pco_profile = pco_profile_form.save(commit=False)

How can I connect the user to a post he created in Django

I am starting with Django, and I have a question about the connection between a post and the user who created it. For now, I managed to create the link, however, whenever I create a new post, the user id is always the default one, thus one. I want to make it in a way that the user id is the id of the person creating the post, and for some reason, it never works. The other option I tried is to put "user" into the form but the problem is that then the user can choose which user he is, which is risky. So is there any way to make it automatic? That when the post is created, the right user id is directly connected to it? Thank you for any help!!
model.py
"""
class Post(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE, default=1)
image = models.ImageField(default="man.jpg")
titre = models.CharField(max_length=50)
slug = models.SlugField(max_length=100)
date_publication = models.DateTimeField(auto_now_add=True)
"""
view.py
"""
#login_required
def post_create(request):
if request.method == "POST":
post_form = PostForm(request.POST)
if post_form.is_valid():
post_form.save()
messages.success(request, 'Your post was successfully created!')
return redirect('seed:view_seed')
else:
messages.error(request, 'Please correct the error below.')
else:
post_form = PostForm(request.POST)
return render(request, "post/create.html", context={"post_form": post_form})
"""
forms.py
"""
class PostForm(ModelForm):
class Meta:
model = Post
fields = ["user", "image", "titre", "slug"]
"""
You remove the user field from the fields in the form:
class PostForm(ModelForm):
class Meta:
model = Post
# no user &downarrow;
fields = ['image', 'titre', 'slug']
and in the view you add the logged in user to the instance wrapped in the form:
#login_required
def post_create(request):
if request.method == 'POST':
post_form = PostForm(request.POST)
if post_form.is_valid():
# add user to the instance &downarrow;
post_form.instance.user = request.user
post_form.save()
messages.success(request, 'Your post was successfully created!')
return redirect('seed:view_seed')
else:
messages.error(request, 'Please correct the error below.')
else:
post_form = PostForm()
return render(request, "post/create.html", context={"post_form": post_form})
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

IntegrityError at /accounts/signup in django when trying to signup

this is my blog : http://gorani-dncvb.run.goorm.io/
I am trying to build a signup page for my django blog.
I finished writing codes for template/view/form/url, and successfully connected to the page : http://gorani-dncvb.run.goorm.io/accounts/signup.
So I came to think there is no problem in template/url. but the problem arises after trying signup, It saids :
IntegrityError at /accounts/signup
UNIQUE constraint failed: auth_user.username
and this is my view code :
def signup(request):
if request.method == "POST":
form = SignupForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
raw_password = form.cleaned_data.get('password')
user = User.objects.create_user(username=username, password=raw_password)
return redirect("post_list")
else:
form = SignupForm()
return render(request, 'registration/signup.html', {'form':form})
and this is form code :
class SignupForm(forms.ModelForm):
class Meta:
model = User
fields = ('username','password',)
(I didn't used UserCreateForm on purpose)
There's definitely no overlapping another user, so why I'm seeing this error message?
The reason for duplicating the user is that django creates the user when saving the form.
When a form is a child of ModelForm, saving it will create a new object of the model class related to the form.
Just delete or comment the code lines for user creation and it wil work fine:
def signup(request):
if request.method == "POST":
form = SignupForm(request.POST)
if form.is_valid():
user = form.save()
user.set_password(user.password)
user.save()
return redirect("post_list")
else:
form = SignupForm()
return render(request, 'registration/signup.html', {'form':form})
Best regards.

RelatedObjectDoesNotExist for a model with foreginkey with standard user model

I have this issue :RelatedObjectDoesNotExist
I add a UserProfile to my models (for now there is only avatar to be added to User Model.
It is not mandatory to add a picture. So some userProfile are null. The proble is when I edit Userform and ProfileUserform. I had an error :
RelatedObjectDoesNotExist at /accounts/user_edit/
User has no profile.
I try to add a try: except in views but seems not working
Models.Py:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') # il s'agit d'ajouter des infos au modele User
# add any additional attributes needed
avatar = models.ImageField(upload_to='profile_pics', blank=True) # avatar of the user in director profile_pics
def __str__(self):
return self.user.email
Views.py:
#login_required(login_url='account/sign_up.html')
def user_edit(request):
# Get info from "both" forms
# It appears as one form to the user on the .html page
user_form = UserFormEdit(instance=request.user)
profile_form = UserProfileForm(instance=request.user.profile)
# Check to see both forms are valid
if user_form.is_valid() and profile_form.is_valid():
# Prepare Save User Form to Database
user = user_form.save()
profile = profile_form.save(commit=False)
# Check if they provided a profile picture
if 'profile_pic' in request.FILES:
print('found it')
# If yes, then grab it from the POST form reply
profile.profile_pic = request.FILES['profile_pic']
profile.save()
messages.success(request, 'User updated')
return render(request, 'account/user_edit.html',
{'user_form': user_form,
'profile_form': profile_form}
)
Error is on profile_form = UserProfileForm(instance=request.user.profile)
because request is null....
I would like to have possibility to add or edit UserProfile info after creation of User. So it should render a empty UserProfileForm if it does not already exists or instance user.profile data if exists
thanks for helping
You can check out the profile exists, and if not use a new instance.
try:
profile = request.user.profile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
profile_form = UserProfileForm(instance=profile)
The fact that you have a RelatedObjectDoesNotExist error originates from the fact that a user does not per se has a related Profile. So request.user.profile can fail.
You can fetch the Profile if it exists with .filter(..).first():
#login_required(login_url='account/sign_up.html')
def user_edit(request):
user = request.user
profile = Profile.objects.filter(user=user).first()
if request.method == 'POST':
user_form = UserFormEdit(request.POST, request.FILES, instance=request.user)
profile_form = UserProfileForm(request.POST, request.FILES, instance=profile)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.instance.user = user
profile = profile_form.save()
messages.success(request, 'User updated')
return redirect('some_view')
else:
user_form = UserFormEdit(instance=user)
profile_form = UserProfileForm(instance=profile)
return render(
request,
'account/user_edit.html',
{'user_form': user_form, 'profile_form': profile_form}
)
Furthermore a form can handle files as well, and I advice you to let the form handle the work, since it will usually be less error-prone, perform proper validations, and remove a lot of boilerplate code.
While it is completely fine to have two related models with an object existing only in one, in this case, it might be better to always have a profile even if it is empty. I would suggest creating a user profile on user create.
Anyways if you want to continue with the existing code, a simple way to handle creating a UserProfile on edit would be to do something like the below, in your view
try:
profile = request.user.profile
except RelatedObjectDoesNotExist: # I feel it should actually throw AttributeError here
profile = UserProfile(user=request.user) # this is a dummy profile and won't be save until the form is saved
profile_form = UserProfileForm(instance=profile)
If you do want to create a profile on creation of users, you can add it to the save of the form or model.
# in form
def save(self, *args, **kwargs):
is_create = self.instance.pk is None
return_value = super().save(*args, **kwargs)
if is_create:
UserProfile.objects.create(user=self.instance)
return return_value
# or in user model
def save(self, *args, **kwargs):
is_create = self.pk is None
super().save(*args, **kwargs)
if is_create:
UserProfile.objects.create(user=self)
return self
# or in the view
def create(request):
...
if form.is_valid():
user = form.save()
UserProfile.objects.create(user=self)
...

Avoid access to other pages via URL, if not logged on (Django)

Team, I have used Django Authentication models which is validating the login to the my blog, but it still permits users to access other restricted pages through url, I need to avoid this, please help on that. Please add maximum details as you can, I am struggling a lot with that
Models:
from django.db import models
from django.db.models import permalink
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
datposted = models.DateTimeField('date posted')
category = models.ForeignKey('Category')
owner = models.ForeignKey('UserProfile')
def __str__(self):
return '%s' % self.title
class Category(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_images', null=True)
def __unicode__(self):
return self.user.username
class Logout(User):
force_logout_date = models.DateTimeField(null=True, blank=True)
Views:
def index(request):
template = "index.html"
return render(request,template)
def menu(request):
return render(request,"menu.html")
def view_posts(request):
return render_to_response('posts.html',{'posts':Post.objects.all()})
def view_post(request, post_id=1):
return render_to_response('view_post.html',{'post':Post.objects.get(id=post_id)})
def view_by_year(request):
cur_year=timezone.now().year
posts_cur_year = Post.objects.filter(datposted__year=cur_year)
return render_to_response('view_by_year.html',{'posts_cur_year':posts_cur_year})
def view_by_month(request):
cur_month=timezone.now().month
posts_cur_month = Post.objects.filter(datposted__month=cur_month)
return render_to_response('view_by_month.html',{'posts_cur_month':posts_cur_month, 'cur_month':cur_month})
def view_by_owner(request):
user = request.user
posts_owner = Post.objects.filter(owner__user=request.user)
return render_to_response('view_by_owner.html',{'view_owner':posts_owner})
def register(request):
# Like before, get the request's context.
context = RequestContext(request)
# A boolean value for telling the template whether the registration was successful.
# Set to False initially. Code changes value to True when registration succeeds.
registered = False
if request.method == 'POST':
# Attempt to grab information from the raw form information.
# Note that we make use of both UserForm and UserProfileForm.
user_form = UserForm(data=request.POST)
profile_form = UserProfileForm(data=request.POST)
# If the two forms are valid...
if user_form.is_valid() and profile_form.is_valid():
# Save the user's form data to the database.
user = user_form.save()
# Now we hash the password with the set_password method.
# Once hashed, we can update the user object.
user.set_password(user.password)
user.save()
# Now sort out the UserProfile instance.
# Since we need to set the user attribute ourselves, we set commit=False.
# This delays saving the model until we're ready to avoid integrity problems.
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
registered = True
else:
print user_form.errors, profile_form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
user_form = UserForm()
profile_form = UserProfileForm()
# Render the template depending on the context.
return render_to_response(
'register.html',
{'user_form': user_form, 'profile_form': profile_form, 'registered': registered},
context)
def user_login(request):
# Like before, obtain the context for the user's request.
context = RequestContext(request)
# If the request is a HTTP POST, try to pull out the relevant information.
if request.method == 'POST':
# Gather the username and password provided by the user.
# This information is obtained from the login form.
username = request.POST['username']
password = request.POST['password']
# Use Django's machinery to attempt to see if the username/password
# combination is valid - a User object is returned if it is.
user = authenticate(username=username, password=password)
# If we have a User object, the details are correct.
# If None (Python's way of representing the absence of a value), no user
# with matching credentials was found.
if user:
# Is the account active? It could have been disabled.
if user.is_active:
# If the account is valid and active, we can log the user in.
# We'll send the user back to the homepage.
login(request, user)
return HttpResponseRedirect('/menu/')
else:
# An inactive account was used - no logging in!
return HttpResponse("Sua conta nao esta ativa.")
else:
# Bad login details were provided. So we can't log the user in.
print "Credenciais Incorretas: {0}, {1}".format(username, password)
return HttpResponse("Login invalido.")
# The request is not a HTTP POST, so display the login form.
# This scenario would most likely be a HTTP GET.
else:
# No context variables to pass to the template system, hence the
# blank dictionary object...
return render_to_response('login.html', {}, context)
def create_post(request):
if request.method == 'POST':
form = CreatePostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.datposted = datetime.datetime.now()
#post.owner = request.user()
post.save()
return HttpResponseRedirect('/posts/')
else:
return HttpResponse("Favor. Verifique os campos necessarios")
else:
form = CreatePostForm()
f = {'form' : form}
return render(request,'create_post.html',f)
def logout(request):
auth.logout(request)
return render_to_response('logout.html')
You can use the #login_required decorator above each view that you want to protect:
#login_required
def index(request):
template = "index.html"
return render(request,template)
This will ensure that the user has logged in before allowing them access to each view that utilizes this decorator.
See the Documentation for more information.

Categories