Can't change user profile image in django - python

I am really new to django and I am building a website for internal use in my company. I have extended the user model with another model called "profile" in order to store extra information about each user, including a profile picture.
I have set up a form.py class with the data i'd like to be able to modify:
class UserForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['office','role', 'userImage']
The form in the html is as follows:
<form class="form" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form__group">
<label for="Profile.userImage">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form__action">
<a class="btn btn--dark" href="{% url 'user-profile' request.user.id%}">Cancel</a>
<button class="btn btn--main" type="submit">Update</button>
</div>
</form>
And in the views.py, here is the function that takes care of this:
def update_user(request):
user = request.user
profile = request.user.profile
if request.method == 'POST':
form = UserForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
return redirect('user-profile', pk=user.id)
else:
form = UserForm(instance = profile)
return render(request, 'base/update-user.html', {'form': form})
And the profile model is:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
job= models.TextField(max_length=50, blank=True, verbose_name="Centro")
role= models.TextField(null=True, max_length=50, blank=True, verbose_name="Cargo")
userImage = models.ImageField(upload_to='profileImages/', default='profileImages/happy.jpg', verbose_name="Profile Image")
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
in addition, the MEDIA_ROOT and MEDIA_URL are as follows:
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images') MEDIA_URL = '/images/'
The form works fine for all fields, they both dispplay and update all the information regarding the "office" and "job" fields, but not the profile image.. It neither uploads a file to the folder I have assigned it to upload to. Even when I manually insert another image in the folder, it doesn't change it. Everything works through django admin though. Images are uploaded and changed correctly. It just wont do it via the form, no errors come up and the terminal doesn't display any issues, neither does the console in the browser.
I don't know what to do, please let me know if you need any extra information in order to diagnose the problem.
I appreciate any help provided!

Related

How to extend django UserCreationForm model to include phone number field

I cant seem to find any posts here regarding extending the Django UserCreationForm model to include a phone number field for users to enter their number and then validate the phone number using phonenumbers.parse in the backend to check if the number is in the respective format and whether it exists or not. I need to know what code I should include in my forms.py under my "users" app.
I've tried including normal html text field for the phonenumbers and it does not belong to the default UserCreationForm model in Django and neither can it be stored in the database. (I need it to be stored in the database for the phone numbers). I am only using forms.py, views.py and register.html to be rendered in views as shown below, currently I am not using models.py.
/* forms.py */
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# from validate_email import validate_email
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
phone_number = forms.IntegerField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
/* views.py */
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Account created for {username}!')
return redirect('blog-home')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
/* register.html */
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign
Up</button>
</div>
</form>
<div class="border-top pt-3">
<small class="text-muted">
ALready Have An Account? <a class="ml-2" href="#">Sign In</a>
</small>
</div>
</div>
{% endblock content %}
I need to include a phone number field as part of the UserCreationForm in django and validate the number to check if it exists or not and then save the number in the database.
I usually extend Django User Model using OneToOneLink
models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=11, blank=True) # change the field to watever works for you
# This will auto create a profile of user with blank phone number that can be updated later.
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('phone')
views.py
def create_user(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.user_profle)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, _('New user created successfully'))
return redirect('settings:profile')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.user_profile)
return render(request, 'template_name.html', {
'user_form': user_form,
'profile_form': profile_form
})
template:
<form method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<button type="submit">Save changes</button>
</form>
UPDATE to initial answer. I decided while the solution below worked it was preferable to inherit the AbstractUser model and add my own requirements in my own bespoke user model. Once in place it's far more straight forward dealing with views and templates. I hesitated at first as I didn't feel confident enough to mess around with the default User model, but it is actually very simple. It also helped me understand abstracting models in general via.
class Meta:
abstract = True
The posts here were very helpful.
How to Extend Django User model using AbstractUser
Extending User Model
Previous post:
I've been struggling with this issue also. I've found a solution that works ok, but may have some pitfalls, which it would be good to get views on. My solution is a combination of the other answer to this question, with two modifications. Firstly the code above should be used to update a user rather than create one, because at registration no user profile exists so can't be called. Secondly, I removed the create_user_profile method on the model and used the answer posted here How to Extend UserCreateForm
to save the extended user information at registration. The reason for removing the create_user_profile was to prevent interference with the save() method on the form. The extended model i'm using is called Account.
I also found this article useful extending the django user model, and I'm still considering whether one of the other options might be more appropriate.
My code looks like this:
Views:
def register_view(request):
form = AccountRegisterForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("accounts:login")
context = {"form": form}
return render(request, "accounts/register.html", context)
def user_update_view(request):
user_obj = User.objects.get(username=request.user)
account_obj = Account.objects.get(user=request.user)
user_form = UserForm(request.POST or None, instance=user_obj)
account_form = AccountForm(request.POST or None, instance=account_obj)
if user_form.is_valid() and account_form.is_valid():
user_form.save()
account_form.save()
return redirect(reverse("accounts:detail"))
context = {
"account_form": account_form,
"user_form": user_form,
}
return render(request, "accounts/account_update.html", context)
Forms
class AccountRegisterForm(UserCreationForm):
group = forms.ModelChoiceField(queryset=Group.objects)
dir = forms.ModelChoiceField(queryset=Directorate.objects)
class Meta:
model = User
fields = (
"username",
"first_name",
"last_name",
"group",
"dir",
)
def save(self, commit=True):
if not commit:
raise NotImplementedError(
"Can't create User and UserProfile without database save"
)
user = super(AccountRegisterForm, self).save(commit=True)
user_account = Account(
user=user,
group=self.cleaned_data["group"],
dir=self.cleaned_data["dir"],
)
user_account.save()
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ("username", "first_name", "last_name")
class AccountForm(forms.ModelForm):
class Meta:
model = Account
fields = (
"group",
"dir",
)

Django update view won't save

I have this update view but it will not save upon form submission. 5 minutes ago everything was working fine and then I added the edit feature for user posts and all of a sudden nothing will save when trying to edit things.
users app views:
class UserEditProfileView(LoginRequiredMixin,UpdateView):
login_url = '/login/'
model = UserProfile
fields = [
'first_name',
'profile_pic',
'location',
'title',
'user_type',
'website',
'about',
'twitter',
'dribbble',
'github'
]
template_name_suffix = '_edit_form'
def get_success_url(self):
userid = self.kwargs['pk']
return reverse_lazy('users:user_profile',kwargs={'pk': userid})
users app models:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50,default='User')
join_date = models.DateTimeField(default=timezone.now)
profile_pic = models.ImageField(upload_to='profile_pics',null=True,blank=True)
location = models.CharField(max_length=150)
title = models.CharField(max_length=250)
user_type = models.IntegerField(choices=USER_TYPE_CHOICES,default=1)
website = models.URLField(max_length=100,blank=True)
about = models.TextField(max_length=500,default='about')
twitter = models.CharField(max_length=50,blank=True)
dribbble = models.CharField(max_length=50,blank=True)
github = models.CharField(max_length=50,blank=True)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
def __str__(self):
return self.user.username
user profile_edit_form.html:
{% extends "users/base.html" %}
{% block content %}
<div class="form-title">
<h2 class="form-title-text">Edit Profile</h2>
</div>
<div class="user-forms-base">
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
</div>
{% endblock %}
I am having the same issue with updating posts on the home page, however I am assuming the issues are the same and so I'll be able to just replicate this solution over there.
Someone mentioned this in the comments but basically the form was returning in valid so I override form_invalid to print any errors that may have been causing it. This showed that it was sending a string when it was expecting an int at the model level. Once I switched it back to send an int the problem went away and now it works. Thanks guys.

Can't Upload Image In Django

I have a profile page where I would like to allow a user to upload a profile picture. I can edit all of the text but cannot upload an image. It works if i add the image via the Admin, but not via the user's profile page on the website. Note that when created via admin--it is uploading correctly to the directory (profile_image) that i've specified in media folder. I've created some error handling on template page, but the error generated is this: "The 'image' attribute has no file associated with it." Below is my code:
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(default='',max_length=100 )
last_name = models.CharField(default='',max_length=100)
email = models.CharField(max_length=100, default='')
date_birth = models.DateField(default=datetime.datetime.now())
bio = models.TextField(default='')
image = models.ImageField(upload_to='profile_image', blank=True)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
views.py
#login_required
def edit_profile(request):
profile = get_object_or_404(models.UserProfile)
if request.method == 'POST':
form = forms.EditProfileForm(data=request.POST, instance=profile)
if form.is_valid():
form.save()
return redirect('/accounts/profile')
else:
form = forms.EditProfileForm(instance=profile)
args = {'form':form}
return render(request, 'accounts/edit_profile.html', args)
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserChangeForm
from . import models
class UserProfileForm(forms.ModelForm):
class Meta:
model = models.UserProfile
fields = [
'first_name',
'last_name',
'email',
'date_birth',
'bio',
'image',
]
class EditProfileForm(UserProfileForm):
model = models.UserProfile
fields = [
'first_name',
'last_name',
'email',
'date_birth',
'bio',
'image',
]
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'accounts/media')
edit_profile.html
{% extends "layout.html" %}
{% block title %}User Profile | {{ user }}{{ super }}{% endblock %}
{% block body %}
<h1>My Profile</h1>
<form action="" method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p}}
<button type="submit">Save</button>
</form>
{% endblock %}
The file comes in request.FILES, not request.POST. Do the following in the view:
form = forms.EditProfileForm(data=request.POST, files=request.FILES, instance=profile)
See the documentation on file uploads:
A view handling this form will receive the file data in request.FILES, which is a dictionary containing a key for each FileField (or ImageField, or other FileField subclass) in the form.
Please check your folder permissions also, DRF is not throwing any permission errors for me.
For Mac use : sudo chmod 777 *
For Live Server don't use this just check what is relevant and secure for your Os.

Cannot upload profile picture in django model

I want the user to upload the profile picture on the profile page but it is not storing it in the media/documents folder, and yes, I have put enctype="multipart/form-data" in the html form and the method is post. I'm new to django so please provide a simple solution
models.py
class User(models.Model):
first_name=models.CharField(max_length=20)
last_name=models.CharField(max_length=20)
username=models.CharField(max_length=25, primary_key=True)
password=models.CharField(max_length=15)
email_id=models.CharField(max_length=30, default='NULL')
profile_pic=models.ImageField(upload_to='profilepics/%Y/%m/%d/',height_field=200,width_field=200,default='')
forms.py
class ProfilePicForm(forms.ModelForm):
class Meta:
model=User
fields=['username','profile_pic']
views.py
def upload(request):
if request.method == 'POST':
username=request.POST['username']
m=User(username=username)
m.profile_pic=request.FILES['profile_pic']
m.save()
return render(request,'LoginPage/done.html')
else:
pic=ProfilePicForm()
return render(request,'AfterLogin/profile.html')
html file
<form method="POST" enctype="multipart/form-data" action="{% url 'LoginPage:upload' %}">
{% csrf_token %}
<p>Upload your profile photo</p><br>
<input id="id_image" type="file" class="" name="image">
<input type="hidden" name="username" value="{{ username }}">
<input type="submit" value="Submit"/>
</form>
Have a look at this:
Need a minimal Django file upload example
Also, try sharing the error you are getting when trying to upload picture.
I think it would be better for you to use the standard User model created by Django which already has the fields first_name, last_name, username, password and email. Then you create a new model with a OneToOneField with the model user.
If the image uploads and if you get a 404 when going directly to the image url when running the server, then you have forgotten to serve the image, which you have to do when you are in production phase.
urlpatterns = [
...patterns...
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Something like this should work:
modles.py
from django.contrib.auth.models import User
class UserPicture(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
picture = models.ImageField(upload_to='...')
forms.py
class ProfilePicForm(forms.ModelForm):
class Meta:
model = UserPicture
fields=['profile_pic']
views.py
def your_view(request):
...
if request.method == 'POST':
form = UserPicture(request.POST, request.FILES)
if form.is_valid():
userprofile = form.save()
userprofile.user = request.user
userprofile.save()
...
You don't have to define own User model since Django has it's own: https://docs.djangoproject.com/en/1.10/ref/contrib/auth/#user-model
And as Jonatan suggested - post error code. If there's none, remove this try ... except: pass.

Attaching a current User object to Django form

I am working on an app that has a section with with a file upload form for .txt fiels. I would like for the current user that is uploading the file to be added along with the file and the file name. Currently, I can do this successfully in the admin section but I just cant get it to save via the form itself. Any Ideas?
Here are the models:
class UploadedTextFile(models.Model):
file = models.FileField(upload_to="textfiles")
filename = models.CharField(max_length = 50)
username = models.ForeignKey(User, blank=True, null=True)
class UploadedTextFileForm(ModelForm):
class Meta:
model = UploadedTextFile
fields = ['file', 'filename']
Here is my view:
def inputtest(request):
#response for file being submited
if request.method == "POST":
form = UploadedTextFileForm(request.POST)
if form.is_valid():
new_form = form.save(commit=False)
new_form.username = request.user
new_form.save()
return render(request, 'about.html')
inputtest = UploadedTextFileForm()
return render(request, 'failed.html', {'inputtest': inputtest})
else:
inputtest = UploadedTextFileForm()
return render(request, 'inputtest.html', {'inputtest': inputtest})
Here is my html:
{% extends 'base.html' %}
{% block content %}
<form method="post">{% csrf_token %}
{{ inputtest.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock content %}
Doing it in the view (as you've shown) is the right way to do this. Most likely you're having problems because you've left username as a field on the form, and because the FK model field doesn't have blank=True set the form requires the field to be provided. You should explicitly declare just the subset fields that you want to accept user input for in the form's Meta class.
class UploadedTextFileForm(ModelForm):
class Meta:
model = UploadedTextFile
fields = ['file', 'filename']
I am not sure why you're rendering a different template when the form is not valid, but no matter what you're not providing the form object in the context. This means that you'll never see any errors the form detects, which is probably what's happening with this code - you're not seeing the error that username is not provided.

Categories

Resources