RelatedObjectDoesNotExist for a model with foreginkey with standard user model - python

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)
...

Related

Django request.user not showing correct user in frontend

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)

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.

Django: Having trouble with users uploading files, and updating records in the db

For my django project, I am using a custom user model, and this works just fine. I also have a page that lets a user edit their own profile, and this also works correctly.
I just added an ImageField field to my user model for an avatar image. However I am having trouble with letting the user update the image themselves. It works fine in the admin interface, but not my user form.
Here is the relevant part models.py
# Generates the path for uploaded avatar images
def upload_avatar_path(instance, filename):
ext = filename.split('.')[-1]
return 'user/{0}/avatar.{1}'.format(instance.username, ext)
# Users
class User(AbstractBaseUser):
"""
Custom user model for amvl.
"""
username = models.CharField(
unique=True,
max_length=50,
)
avatar = models.ImageField(
upload_to=upload_avatar_path,
default='user/default.png'
)
bio = models.TextField()
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ['avatar', 'bio']
As you can see, the form am using for a user to edit their profile is a ModelForm.
Here is the views.py
def profile_edit(request, profile):
# Check if logged in user is the same as the profile edit page
if request.user.username == profile:
profile = User.objects.get(username=request.user.username)
# If this is a form submission
if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES)
if form.is_valid():
try:
update = ProfileUpdateForm(
request.POST,
request.FILES,
instance=profile,
)
update.save()
return HttpResponse("UPDATED")
except:
return HttpResponse('FAILED')
else:
form = ProfileUpdateForm(instance=profile)
context = {
'form': form,
}
return render(request, 'user/profile_edit.html', context)
else:
return HttpResponse("NOT ALLOWED")
Everything works correctly here, except for updating the avatar field. Interestingly, when I submit the form "UPDATED" is returned, indicating that it worked. But when I check, the avatar image is not updated, even if other fields are.
Other examples I've seen seem to suggest that I need to use request.FILES['avatar'] to save the record, but when I do, it returns "FAILED".
When I remove the try and except statements to view the debug info, its shows a MultiValueDictKeyError at /user/admin/edit/, "'avatar'". (When I use request.FILES['avatar'])
According to the Django documentation for file uploads, and since you’re working with a ModelForm, you should use:
if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/success/url/')
In case you’re not using a ModelForm…
…but you’re building one of your own, use:
if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES)
if form.is_valid():
instance = User(avatar=request.FILES['avatar'])
instance.save()
return HttpResponseRedirect('/success/url/')
Now, if it still raises a MultiValueDictKeyError (I’m actually not sure of which to give to the key), try catching the exception to get the keys of request.FILES:
try:
instance = User(avatar=request.FILES['avatar'])
except MultiValueDictKeyError:
raise KeyError('Keys for `request.FILES` are {}'.format(request.FILES.keys()))
I've figured it out. It had nothing to do with my python code or Django, my HTML form was missing it's enctype="multipart/form-data" attribute.

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