I am making a website and would like to make it possible for each user to upload and update his own profile picture.
The update-profile template loads perfectly fine. I can update all the other fields, except for the image field. It can only be updated from the /admin page, but not from the update-profile page.
This is my image field in the Profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField(default=datetime.date(1989, 12, 25))
gender = models.CharField(max_length=12, choices=GENDER_CHOICES, default='Unspecified')
city = models.CharField(max_length=100, default='Braşov')
image = models.ImageField(upload_to='profilepics', blank=True)
Here is my ProfileForm:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('dob', 'gender', 'city', 'image')
This is the html template:
{% block body %}
<form method="post">
<div class="container">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<button type="submit">Update</button>
Cancel
</form>
</div>
{% endblock %}
And this is the update_profile view:
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, ('Your profile was successfully updated!'))
return redirect('myprofile')
else:
messages.error(request, ('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.profile)
return render(request, 'filter/updateprofile.html', {
'user_form': user_form,
'profile_form': profile_form
})
Thank you very much for reading!
Add the attribute enctype to your form tag in your html document enctype="multipart/form-data"
See this question
Django ModelForm Imagefield Upload
And as is documented in w3schools
https://www.w3schools.com/tags/att_form_enctype.asp
Related
I'm trying to make inline form by using inlineformset_factory but my Image object is not getting saved
models:
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
availability = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.name
class Image(models.Model):
file = models.ImageField(upload_to="products_images/", default="static/default.png")
uploaded = models.DateTimeField(auto_now_add=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
views:
def CreateNewProductView(request):
context = {}
ProductObj = None
form=ProductForm()
if request.method=='POST':
form = ProductForm(request.POST)
if form.is_valid():
ProductObj = form.save()
print('form is valid, product has been created')
else:
print("form is not valid")
ImageFormset = inlineformset_factory(Product, Image, fields=('file',), extra=1, can_delete=False)
if request.method=='POST':
formset = ImageFormset(request.POST, request.FILES, instance=ProductObj)
if formset.is_valid():
formset.save()
print('formset is valid, product has been created')
else:
print("formset is not valid")
else:
formset = ImageFormset(instance=ProductObj)
if form.is_valid() and formset.is_valid():
return redirect('home')
context = {'form': form, 'formset':formset}
return render(request, 'Ecommerce/test.html', context)
template test.html
{% extends 'base.html' %}
{% block content %}
<form method="POST" action="" id="image-form" style="padding-top:10px;">
{% csrf_token %}
{{form.as_p}}
{{formset}}
{{formset.management_form}}
<button type="submit">submit</button>
</form>
{% endblock content %}
In console I can see "formset is valid, product has been created"
When I printed (request.FILES) i saw <MultiValueDict: {}>. Should it be like that ? In django admin pannel there is no Image objects
What am I doing wrong ?
Add this to your HTML form tag to send files to the server:
enctype="multipart/form-data"
Easy to forget.
Your form will then look like:
<form method="POST" enctype="multipart/form-data" action="" id="image-form" style="padding-top:10px;">
...
Link to Django-docs
I use following code:
models.py
class Profile(models.Model):
location = models.CharField(max_length=300, blank=True)
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
)
def __str__(self):
return self.imie
views.py
def edit(request):
if request.method == 'POST':
profile = Profile.objects.get(user = request.user)
profile_form = ProfileForm(request.POST, instance = profile)
if profile_form.is_valid():
profile_form.save()
messages.success(request, ('Your profile was successfully updated!'))
return redirect('profile')
else:
messages.error(request, ('Please correct the error below.'))
else:
profile = Profile.objects.get(user = request.user)
profile_form = ProfileForm(request.POST, instance = profile)
return render(request, 'edit.html', {
'profile_form': ProfileForm
})
forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('location')
edit.html
<div class="col-md-6 offset-md-3">
<form method="post">
{% csrf_token %}
{{ profile_form.as_p }}
<button type="submit" class="btn btn-secondary">Save changes</button>
</form>
</div>
Following code should allow the user to edit data stored in Profile model and it does exactly that, however form is loaded empty (without placeholder) and I would like it to display previous values.
Edit: where the link to edit.html is anchored
it's just a simple verions of a profile page where just plain data is displayed
{% for things in logged_in_user_data %}
<ul>
<li>Your location: {{ things.location}}</li>
</ul>
{% endfor %}
with it's views.py
def profile(request):
logged_in_user_data = Profile.objects.filter(user=request.user)
return render(request, 'profile.html', {'logged_in_user_data': logged_in_user_data})
In the case the request is not a POST request you have still written ProfileForm(request.POST, instance = profile). What does this signify? It means that this form is now a bound form and a user has submitted some data to it (which here was empty since the request was not a post request). This causes the form to be rendered empty since it is considered that the user had provided those empty values. Instead you need to not pass request.POST here:
def edit(request):
profile = Profile.objects.get(user = request.user) # Move common line here
if request.method == 'POST':
profile_form = ProfileForm(request.POST, instance = profile)
if profile_form.is_valid():
profile_form.save()
messages.success(request, ('Your profile was successfully updated!'))
return redirect('profile')
else:
messages.error(request, ('Please correct the error below.'))
else:
profile_form = ProfileForm(instance=profile) # No `request.POST` here
return render(request, 'edit.html', {
# Typo noted as per comment by #A67John
'profile_form': profile_form
})
I want to user have a avatar. So I created the model and the form.
Image is not displaying on the page. But form saves the image to folder. Where is mistake?
request.user.avatar.url doesn't work. Maybe the image is not attached to User? Thanks for the help
P.S. djagno-avatar is not good for me.
models.py
class Avatar(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, null=True)
avatar = models.ImageField(upload_to='user_avatars', null=True)
forms.py
class UserPhotoForm(forms.ModelForm):
class Meta:
model = Avatar
fields = ('avatar', )
widgets = {
'avatar': forms.FileInput(attrs={'class': 'input'}),
}
views.py
def cabinet(request):
user = request.user
if request.method == 'POST':
form = UserPhotoForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.save()
else:
form = UserPhotoForm()
return render(request, 'account/cabinet/cabinet.html', {'form': form})
cabinet.html
<div class="avatar">
{{ request.user.avatar.url }} #there is trying
{{ request.user.avatar.avatar.url }}
<img src="{{ request.user.avatar.avatar.url }}" alt="" width="80px" height="80px">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" id="file"/>
</form>
try this
<img src="{{ request.user.avatar.url }}" alt="" width="80px" height="80px">
https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ImageField
Found the main problem you are never storing your avatar model in db. As you are giving it the instance of your user. Because of that your Avatar model is not stored.
You have to use
def cabinet(request):
user = request.user
if request.method == 'POST':
form = UserPhotoForm(request.POST,request.FILES)
if form.is_valid():
temp=form.save(commit=False)
temp.user = user
temp.save()
else:
form = UserPhotoForm()
return render(request, 'account/cabinet/cabinet.html', {'form': form})
Try that
I have created a page to update User profile. If I try to update a value of user with existing user, error is thrown as expected, however the variable user.username in profile.html shows the value I am trying to update. My query is why {{ user.username }} is picking up the incorrect value even though the save() method is not called.
profile.html
<div class="content p-3">
<div><img class="rounded-circle" src="{{ user.profile.image.url }}" width="100" height="100"></div>
<div>{{ user.username }}</div>
<div>{{ user.email }}</div>
</div>
<div class="w-25">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend>Profile Info</legend>
{{ user_form | crispy }}
{{ profile_form | crispy }}
</fieldset>
<input class="mt-3" type="submit" value="Update">
</form>
</div>
forms.py
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
views.py
#login_required
def profile(request):
if (request.method == "POST"):
user_form = UserUpdateForm(request.POST, instance=request.user)
profile_form = ProfileUpdateForm(request.POST,
request.FILES,
instance=request.user.profile)
if (user_form.is_valid() and profile_form.is_valid()):
user_form.save()
profile_form.save()
messages.success(request, f"Your profile is updated successfully")
return redirect("profile")
else:
messages.error(request, f"Failed to update profile")
else:
user_form = UserUpdateForm(instance=request.user)
profile_form = ProfileUpdateForm(instance=request.user.profile)
return render(request, 'User/profile.html', {'user_form': user_form, 'profile_form': profile_form}
The ModelForm updating the instance as part of validation is strange/unwanted behaviour, maybe you have found a "bug"
A work around would be to pass a copy of request.user using copy.copy into the ModelForm so that any changes made as part of validation happen on the copy
user_form = UserUpdateForm(request.POST, instance=copy.copy(request.user))
When you write user_form = UserUpdateForm(request.POST, instance=request.user),
your user_form update itself using the values received from front-end. In this case, username is 'newuser2'.
Since this username is taken, user_form.is_valid() returns false and below render function is returned:
return render(request, 'User/profile.html', {'user_form': user_form, 'profile_form': profile_form}
Please note that this instance of user_form has username as "newuser2". Thats why you see username as "newuser2" instead of your current logged in user.
Apologies for the brevity, I'm learning to post answers.
I'm trying to create an edit form for existing users, I have the User model and I associated to it a profile.
The problem is that the fields of profile are empty in the rendered html, however when I created a new user I filled these fields, and when I enter to administration I find the fields are filled.
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
DEPARTMENT_CHOICES = (('MI', 'Math et info'),
('ST', 'Science et Tech'),
('SM', 'Science de la matiere'))
user = models.OneToOneField(User, on_delete=models.CASCADE)
teacher = models.BooleanField(default=False)
description = models.TextField(blank=True)
department = models.CharField(max_length=35, choices=DEPARTMENT_CHOICES, blank=True)
picture = models.ImageField(upload_to='profile-images', blank=True)
def __str__(self):
return self.user.username
views.py
def profile_view(request):
if request.method == 'POST':
user_form = EditUserForm(request.POST, instance=request.user)
ins = Profile.objects.get(pk=5)
profile_form = EditProfileForm(request.POST, request.FILES, instance=ins)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
return redirect(home)
else:
user_form = EditUserForm(instance=request.user)
profile_form = EditProfileForm(request.FILES, instance=request.user)
return render(request, 'account/profile.html', {'user_form': user_form,
'profile_form': profile_form})
forms.py
class EditProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('description', 'department', 'picture', )
class EditUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', )
profile.html
{% extends 'manhal/base.html' %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-6">
<form method="post" enctype="multipart/form-data" action="{% url 'profile' %}" class="form-horizontal">{% csrf_token %}
<fieldset>
<legend>User Profile</legend>
{{ user_form|crispy }}
{{ profile_form|crispy}}
<input type="submit" value="Save" class="btn btn-primary">
</fieldset>
</form>
</div>
{% endblock %}
First, your if/else block is checking if the request is a POST. Since the else block is not a POST, you do not want to pass any POST data into your form. This will make the form think it's bound with no data.
Also, it looks like you are passing the request.user to your ProfileForm as the instance, but the model on the ProfileForm meta class is expecting a Profile object.
Can you fix those two things and see if it works or not? If it doesn't work, please post some more code (like your templates).