Django: How can I update the profile pictures via ModelForm? - python

I'm trying to write a small Django system. After logging into the system, a user can edit and save his/her own profile information. The fields involved are: username, email, first name, last name, website and picture.
The problem: The picture cannot be updated (After selecting an image and clicking "update" button, it shows "No file selected". The profile picture displayed on the page is still the old one). But the other fields are all OK.
Here are my codes:
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User)
website = models.URLField(blank=True)
picture = models.ImageField(upload_to="profile_images", blank=True)
def __str__(self):
return self.user.username
forms.py:
class UserForm2(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('website', 'picture')
views.py:
#login_required
def update_user(request):
try:
user_profile = UserProfile.objects.get(user=request.user)
except UserProfile.DoesNotExist:
return HttpResponse("invalid user_profile!")
if request.method == "POST":
update_user_form = UserForm2(data=request.POST, instance=request.user)
update_profile_form = UserProfileForm(data=request.POST, instance=user_profile)
if update_user_form.is_valid() and update_profile_form.is_valid():
user = update_user_form.save()
profile = update_profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
else:
print(update_user_form.errors, update_profile_form.errors)
else:
update_user_form = UserForm2(instance=request.user)
update_profile_form = UserProfileForm(instance=user_profile)
return render(request,
'userprofile/update_user.html',
{'update_user_form': update_user_form, 'update_profile_form': update_profile_form}
)
update_user.html:
<form id="update_user_form" method="POST" action="/userprofile/update_user/">
{% csrf_token %}
{{ update_user_form.as_p }}
{{ update_profile_form.as_p }}
<img src="{{ update_profile_form.instance.picture.url }}" />
<br />
<input type="SUBMIT" name="submit" value="Update"/>
</form>
How can I make it work properly?

To upload the file you should add the enctype attribute to the <form> tag:
<form id="update_user_form" method="POST" action="/userprofile/update_user/"
enctype="multipart/form-data">

Related

How to get Checkbox Select Multiple for profile page

Beginner here in Django.
Goal:
I would like to get a multiple checkbox selection for favorite categories and save those in
the database.
Problem
Right now I have a profile form and model, and the CheckboxSelectMultiple widget is not working like I want it to work. The categories are stored in another model which I reference in the user profile.
# # # # user/forms/profile_form.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
exclude = [ 'id', 'user']
widgets = {
'favourite_categories': widgets.CheckboxSelectMultiple(attrs={'class': 'form-control'}),
'profile_picture': widgets.TextInput(attrs={'class': 'form-control'})
}
# # # # user/models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=200, blank=True)
last_name = models.CharField(max_length=200, blank=True)
favorite_categories = models.ManyToManyField(Category, blank=True)
profile_image = models.CharField(max_length=200, null=True, blank=True)
# # # # user/views.py
# login_required
def profile(request):
user_profile = Profile.objects.filter(user=request.user).first()
if request.method == 'POST':
form = ProfileForm(instance=user_profile, data=request.POST)
if form.is_valid():
user_profile = form.save(commit=False)
user_profile.user = request.user
user_profile.save()
return redirect('profile')
return render(request,
'user/profile.html',
context={"form": ProfileForm(instance=user_profile)})
# # # # templates/user/profile.html
<div class="shadow p-4 mb-5 mx-5 bg-body rounded">
<form class="form form-horizontal" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="btn btn-primary" value="Update" />
</form>
</div>
The output of this can be seen in the picture.
PS. I also don't know how to use ImageField for inserting a profile image. I am using a google cloud database and the ImageField needs the variable upload_to, which specifies where the image will be uploaded to. I would like to store the image on the cloud database as all other data is.
It seems you'd have to write your own custom CheckboxSelectMultiple widget. You can see the answer of this post.

Django - After Register, Data Should Go To 2 Different Tables (Customer & User)

I am creating an e-commerce website where people can choose to login or not but still the can order and checkout (even if you are an AnonymousUser or Guest user). Now, I am making a login and register form in my website. The login form works and looks good but the register form wasn't working and throwing an error that said "RelatedObjectDoesNotExist at / User has no customer."
I think the reason is that when I register, it only makes a User in database but didn't register anything in the Customer table (which consists Name and Email). How can I register a Customer and User at the same time when I hit the "Submit" button? And how can I make that specific User have "Staff status" only and cannot make changes in the Admin site?
Also, I want to add new fields in the Register form for Name and Email that will go directly to the Customer table. I tried to do this one but it doesn't work and throwed and error that says "django.core.exceptions.FieldError: Unknown field(s) (name) specified for User".
Here's what I did:
from django.forms import ModelForm
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import *
class CustomUserCreationForm(UserCreationForm):
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200)
class Meta:
model = User
fields = ['username', 'name', 'email', 'password1', 'password2']
SUMMARY:
I want to add extra fields in the Register form called Name and Email. Then after clicking the Register form, I want create User and Customer at the same time. But the User should only have "Staff status" and cannot make changes in the Admin site. And the Name and Email field should go to Customer Table with the User I've created.
Here's the screenshot of my Register form:
Here's my forms.py file:
from django.forms import ModelForm
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'password1', 'password2']
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update({'class':'form-control','placeholder':'Enter Username'})
self.fields['password1'].widget.attrs.update({'class':'form-control','placeholder':'Enter Password'})
self.fields['password2'].widget.attrs.update({'class':'form-control','placeholder':'Confirm Password'})
Here's my views.py file:
def loginUser(request):
page = 'login'
if request.method == "POST":
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
print('USER:', user)
if user is not None:
login(request, user)
return redirect('/')
return render(request, 'store/login_register.html', {'page': page})
def logoutUser(request):
logout(request)
return redirect('/')
def registerUser(request):
page = 'register'
form = CustomUserCreationForm()
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.save()
user = authenticate(request, username=user.username, password=request.POST['password1'])
if user is not None:
login(request, user)
return redirect('/')
context = {'form': form, 'page': page}
return render(request, 'store/login_register.html', context)
Here's my models.py file:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200)
def __str__(self):
return self.name
Here's my register.html file:
<form class="form" method="POST">
{% csrf_token %}
<h2> REGISTER </h2>
<h4> Create your account now! </h4>
<br />
{% for field in form %}
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">{{field.label}}:</label>
{{field}}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
<br />
<p> Already have an account? Login here </p>
</form>

image not saved while updating userprofile in django

I am trying to update a userprofile model that i used to save additional information over the inbuilt User model, now when i am trying to update it , the image does not gets saved. I need help to resolve this issue
# In views.py
#login_required(login_url=LOGIN_REDIRECT_URL)
def update_user_profile(request):
userobj = get_object_or_404(UserProfile, user=request.user)
form = UserProfileForm(data = request.POST or None,files= request.FILES or None, instance=userobj)
if request.method=='POST':
print(form.is_valid())
if form.is_valid():
profile = form.save(commit=False)
profile.picture = form.cleaned_data['picture']
profile.about = form.cleaned_data['about']
profile.save()
else:
print("NO picure")
return HttpResponseRedirect("/blog/profile/")
return render(request, "blog/post_update.html", {'form':form})
#models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
about = models.CharField(max_length=200, null=True, blank=True)
picture = models.ImageField(upload_to="profile_images/", blank=True)
def __str__(self):
return str(self.user)
#In forms.py
class UserProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
self.fields['about'].widget.attrs.update({'class': 'form-control '})
self.fields['picture'].widget.attrs.update({'class': 'form-control-file'})
class Meta:
model = UserProfile
fields = ('about', 'picture')
# userprofileform.html
{% extends 'base.html' %}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Create Profile">
</form>
{% endblock %}
please take a look at the code and help. while registering if the image was uploaded it get's saved , but when i try to update the userprofile directly in profile section image does not get changed and shows the same as one saved while user registration else it shows None.
I did some changes on templates in settings.py and got my project runnning. Issue was that i was not mentioning the Templates directory properly

Django custom registration form

I'm trying to create a custom form where the user can also enter his name for instance but I am facing an issue, when the registration is done the name is not saved and I can't show it on the template.
here is the code
views.py
def register_user(request):
if request.user.is_authenticated():
return HttpResponseRedirect('/user/')
else:
if request.method == 'POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/user/')
context = {}
context.update(csrf(request))
context['form'] = MyRegistrationForm()
return render(request, 'register.html', context)
forms.py
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
name = forms.CharField(required=True)
class Meta:
model = User
fields = {'name', 'username', 'password1', 'password2', 'email'}
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.name = self.cleaned_data['name']
if commit:
user.save()
return user
register.html
<form action="/user/register/" method="post" id="register" autocomplete="off">
{% csrf_token %}
<div class="fieldWrapper">
{{ form.name.errors }}
{{ form.name.label_tag }}
{{ form.name }}
</div>
[... other form fields ...]
</div>
<input type="submit" value="Register"/>
</form>
So when I submit the form, everything works but when I try to show {{ user.name }} in the template, nothing is shown, why is that? (it works for the email field)
The default User object doesn't have a name field (so you are actually just saving the content of your name field to the object, and not the database). It has a first_name and last_name so you can either use those fields instead or you can customize the User model to have a separate name field
Edit
Also, just so you know, if you use the first_name and last_name fields instead, the User model has a get_full_name() method built-in which might be useful
The User does not have a "name" field. Try:
{{ user.username }}

edit user profile in django without typing password

here I am trying to allow users to make modifications to their user profile. There's a model called UserProfile that holds a one to one relationship to django user itself. Below is the code in views.py
#login_required
def edit_profile(request):
if request.method == 'POST':
profile = UserProfile.objects.get(user=request.user)
profile_form = UserProfileForm(data=request.POST, instance=profile)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
return HttpResponseRedirect("/me/login/")
else:
print user_form.errors, profile_form.errors
else:
user = request.user
profile = UserProfile.objects.get(user=user)
profile_form = UserProfileForm(initial={'website':profile.website,'address':profile.address, 'picture':profile.picture})
return render(request, 'member/edit_profile.html', {'profile_form': profile_form})
However, once the submit button is clicked from the template, I got an error saying that a password is needed.
[27/Apr/2015 14:25:07] "GET /me/edit_profile2/ HTTP/1.1" 200 5080
<ul class="errorlist"><li>username<ul class="errorlist"><li>This field is required.</li></ul></li><li>password<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
[27/Apr/2015 14:25:16] "POST /me/edit_profile/ HTTP/1.1" 200 6384
from the code, I thought that the UserProfile is already bound to a specific user already, and I am only allowing users to make changes on the UserProfile model without touching django's auth User model. I wonder why the username and password is still needed in this case. Would it be possible to allow editing on user profile without users' password?
Here is the UserProfile extended from the User model
class UserProfile(models.Model):
# link user profile to the user models
user = models.OneToOneField(User)
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='avatar', blank=True)
address = models.TextField(blank=True)
#property
def stats(self):
"""get statistics for this profile"""
from tumboon.models import Donation
return Donation.statistics.for_user(self)
#property
def amount_donated(self):
__doc__ = """get the total amount donated """
return self.stats['total_amount_donated']
# Override the __unicode__ to return something meaningful
def __unicode__(self):
return self.user.username
class UserForm(forms.ModelForm):
"""Form for User Registration"""
class Meta:
model = User
fields = ('username', 'email', 'password', 'first_name', 'last_name')
widgets = {
'username' : forms.TextInput(attrs = {'placeholder': 'Username'}),
'email' : forms.TextInput(attrs = {'placeholder': 'Email'}),
'password' : forms.TextInput(attrs = {'placeholder': 'Password'}),
}
class UserProfileForm(forms.ModelForm):
"""Form for UserProfile"""
class Meta:
model = UserProfile
fields = ('website', 'picture', 'address')
And the UserProfileForm on the template is here:
{% if user.is_authenticated %}
<form id="user_profile" class="form-horizontal" method="POST" enctype="multipart/form-data" action="/me/edit_profile/">
{% csrf_token %}
<h3>User Info</h3>
<hr />
<div class="form-group">
<label class="col-xs-2" for="{{ user_form.picture.id_for_label }}">Picture: </label>
<div class="col-xs-10">
{{profile_form.picture|add_class:"form-control"}}
</div>
</div>
<div class="form-group">
<label class="col-xs-2" for="{{ user_form.website.id_for_label }}">Website: </label>
<div class="col-xs-10">
{{profile_form.website|add_class:"form-control"}}
</div>
</div>
<div class="form-group">
<label class="col-xs-2" for="{{ user_form.address.id_for_label }}">Address: </label>
<div class="col-xs-10">
{{profile_form.address|add_class:"form-control"}}</li>
</div>
</div>
<input class="btn btn-default" type="submit" name="save_button" value="Save Profile">
</form>
{% endif %}
Your UserForm class is extending Model.Form class which displays the Password field as required, hence the problem. Use instead the UserChangeForm:
from django.contrib.auth.forms import UserChangeForm
class UserForm(UserChangeForm):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name', 'is_super_admin')
This form handles password as it should.

Categories