how to make profile for each user - django

I have an app that if a user signs up, the app will automatically make a profile page for it, the sign up and login part works correctly but it doesn't make a profile page, I have to do it in the admin page. How should I solve this problem?
this is my models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self):
super().save()
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
this is my views.py
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'Your Acount Has Been Created You Are Now Be Able to Login')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form':form})
#login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
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 Acount Has Been Updated')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form':u_form,
'p_form':p_form
}
return render(request, 'users/profile.html', context)
this is my forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
this is my signals.py
#receiver(post_save, sender=User)
def create_profile(sender, instance, created , **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
and this is my apps.py:
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals

The only reason I can see that would cause this is that the signals aren't registering properly, make sure the config is referenced in the app's init.py file:
# my_app.__init__.py
default_app_config = 'my_app.apps.MyAppConfig'
Alternatively, move the signals to the models.py file and see if they fire when the User is created. As a last resort, try creating the profile in the view when the User form is saved.

Related

Populate custom field in Django form

I would like users to have the ability to update their email address. I created a profile that has fields, but the email address is in the users table. I created a form that adds a custom form field and it works for update. However, I can't find a way to pre-populate this field on a REQUEST.GET.
# forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
class ProfileUpdateForm(ProfileForm):
email = forms.EmailField(max_length=254)
class Meta(ProfileForm.Meta):
fields = ProfileForm.Meta.fields + ('email',)
# views.py
#login_required
#require_http_methods(["GET","POST"])
def profile_update_view(request):
context = {}
# Get the logged in users profile
profile_object = Profile.objects.get(user=request.user.id)
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object)
context["form"] = profile_form
# how can I add User.objects.get(id=request.user.id).email to the custom field
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object)
context["form"] = profile_form
if profile_form.is_valid():
try:
# email address exists
user = User.objects.get(email=profile_form.cleaned_data.get('email'))
messages.error(request, 'Failed profile update. Email address already exists.')
except:
# email address available
# get user object
user = User.objects.get(id=request.user.id)
user.email = profile_form.cleaned_data.get('email')
# update user object
user.save()
profile_form.save()
messages.success(request, 'Successful profile update.')
return render(request, "profile.html", context)
I tend to favour class-based views, and things like this are where they come into their own. The form:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
email = forms.EmailField(max_length=254) #add non-model form field
And a class-based view. Handle the initial value for email in get_initial(), and updating of self.request.user in form_valid():
class ProfileUpdateView( UpdateView):
model = Profile
form_class = ProfileUpdateForm
template_name = 'profile.html' # profiles/update_profile.html would be better
# other declarations ...?
def get_initial(self):
initial = super().get_initial()
initial['email'] = self.request.user.email
return initial
# #transaction.atomic might be a good idea
def form_valid(self, form):
new_email = form.cleaned_data['email']
user = self.request.user
if user.email != new_email: # don't do a pointless non-update save
user.email = new_email
user.save()
return super().form_valid( form) # will save the profile
# forms.py
def __init__(self, *args, **kwargs):
self.email = kwargs.pop("email")
super(ProfileUpdateForm, self).__init__(*args, **kwargs)
self.initial['email'] = self.email
# views.py
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object, email=request.user.email)
context["form"] = profile_form
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object, email=request.POST.get('email'))
context["form"] = profile_form

renaming file when uploading with django modelform

I'm new to django and currently following a tutorial where it uses a ModelForm for a User Profile where a user can upload an avatar. I want to be able to update the file that they upload to a generated id. I'm just not sure where and how to do capture and update the file.
My Model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(default='avatar_default.jpg', upload_to='profile_images')
def save(self,*args, **kwargs):
super().save()
img = Image.open(self.avatar.path)
if img.height > 300 or img.width > 300:
output_size = (300,300)
img.thumbnail(output_size)
img.save(self.avatar.path)
def __str__(self):
return f'{self.user.username} Profile'
Form
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['avatar']
View
#login_required
def user_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 has been updated')
return redirect('users_profile')
else:
user_form = UserUpdateForm(instance=request.user)
profile_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'user_form': user_form,
'profile_form': profile_form
}
return render(request, "users/profile.html", context)
I tried to apply some recommendations online where you do a Commit=False when saving the form and store that in a variable and renaming the file, it was a whole lot of confusion in the end.

Django sort form fields from two models

I have extended the Django user model with some extra fields
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
telephone = models.CharField(max_length=15, blank=True)
email_address = models.CharField(max_length=30, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
#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()
forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('telephone', 'email_address', 'date_of_birth')
widgets = {
'date_of_birth': forms.DateInput(attrs={'type': 'date'}),
}
views.py
def response_form(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
profile, created = Profile.objects.get_or_create(user=request.user)
user_form.save()
profile_form.save()
messages.success(request, ('Your profile was successfully updated!'))
date_of_birth = profile_form.cleaned_data['date_of_birth']
user = profile_form.cleaned_data['user']
context = {
'user': user,
'date_of_birth': date_of_birth
}
template = loader.get_template('thank_you.html')
return HttpResponse(template.render(context, request))
else:
messages.error(request, ('Please correct the error below.'))
else:
user_form = UserForm()
profile_form = ProfileForm()
return render(request, 'response_form.html', {'user_form': user_form, 'profile_form': profile_form})
When the template is loaded it places the user fields above the profile fields.
How can I place the User dropbox above the First name field?
Since you're mixing the order of the fields on two different forms, there's no way to do that whilst still using {{ my_form.as_p }} or similar methods to render your forms in your template.
You have to render your form fields individually, as explained here.
Note that when having multiple forms in one view, it's always wiser to add a prefix to your forms, in order to ensure that field names are unique in your HTML page. In your case, you might have a conflict with the email field.
Also when successfully completing the form, it's standard practice to redirect (302 REDIRECT) your user to the 'thank you' view rather than render the 'thank you' template (200 OK). This is because a page refresh (F5 or cmd-R) will otherwise resubmit the data.

Django, data forms cannot be saved

I have a problem with Django when I would like to get information from the register. I can get their first name, last name, and email but no nickname and graduated schools. I thought i did it correctly since i added nickname and school inside forms.py
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
first_name = forms.CharField()
last_name = forms.CharField()
nickname = forms.CharField()
school = forms.CharField()
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'password1', 'password2',
'first_name', 'last_name', 'nickname', 'school']
class UserUpdateForm(forms.ModelForm):
first_name = forms.CharField()
last_name = forms.CharField()
nickname = forms.CharField()
school = forms.CharField()
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email','first_name', 'last_name', 'nickname', 'school']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
models.py
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300,300)
img.thumbnail(output_size)
img.save(self.image.path)
views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm, UserUpdateForm, ProfileUpdateForm
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'Your account has been created! You are now able to log in')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
#login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
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!')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form
}
return render(request, 'users/profile.html', context)
I have tried to save by adding form.cleaned_data.get in views.py but it still didn't work. I appreciate all help.
The problem is that you are not saving "nickname" and "graduated schools" to any model(based on models.py). First of all, you are inheriting from the user model when you do the following:
'''
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
'''
In doing so you can only save what you have in the Profile model and the User model, which I would recommend reading https://docs.djangoproject.com/en/2.1/ref/contrib/auth/ detailing what does the User model have(username,password, etc.).
Lastly, I would just add the nickname and the graduated schools to the Profile Model and it should work.

Django image upload issue

I'm trying to upload an image. This is an avatar image for the profile of the user.
Currently, the form return no error, but I have nothing written on my database or in my folder media/avatar/.
What's wrong ?
My view :
def view_avatar(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES, instance=request.user.profile)
if form.is_valid():
form.save()
else:
form = UploadFileForm(instance=request.user.profile)
return render(request, 'avatar.html', locals())
My form :
class UploadFileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('avatar',)
My model :
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birthdate = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='media/avatar/', blank=True, null=True)
It's because the form which you are using is inherited from forms.Form , you need to use forms.ModelForm for saving the instance directly.
Change this line,
class UploadFileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('avatar', )
def save(self, *args, **kwargs):
profile, created = Profile.objects.get_or_create(user=self.user)
profile.avatar = self.cleaned_data['avatar']
profile.save()
return profile
Also, edit in your views like this,
if form.is_valid():
file = form.save(commit=False)
file.user = request.user
file.save()
For making a profile you can use signals.
This way whenever a new user been added, a profile will be generated for that user automatically
Your models.py:
from django.conf import settings
from django.db.models.signals import post_save
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
birthdate = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='media/avatar/%y/%m/%d', blank=True, null=True)
def post_save_profile(sender, instance, created, *args, **kwargs):
if created:
try:
Profile.objects.create(user=instance)
except:
pass
post_save.connect(post_save_profile, sender=settings.AUTH_USER_MODEL)
and for updating the information like birthday and avatar you can use ModelForm.
forms.py:
class UploadFileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('birthdate', 'avatar')
def __init__(self, *args, **kwargs):
super(UploadFileForm, self).__init__(*args, **kwargs)
self.fields['avatar'].required = False
your views.py:
def view_avatar(request):
user = request.user
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES, instance=user.profile)
if form.is_valid():
form.save()
for avatar in template you can use this:
<img src="{% if user.profile.avatar %}{{ user.profile.avatar.url }}{% else %}{% static 'no-avatar.jpg' %}{% endif %}"><i></i>
You can write custom Save method for this like this.
View:
def view_avatar(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES, user=request.user)
if form.is_valid():
form.save()
else:
form = UploadFileForm()
return render(request, 'avatar.html', locals())
Form:
class UploadFileForm(forms.Form):
class Meta:
model = Profile
fields = ('avatar', )
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(UploadFileForm, self).__init__(*args, **kwargs)
self.fields['avatar'].required = False
avatar = forms.FileField()
def save(self, *args, **kwargs):
user_profile, created = Profile.objects.get_or_create(user=self.user)
user_profile.avatar = self.cleaned_data.get('avatar')
user_profile.save()
return user_profile
I forgot the enctype !
The solution was :
<form method="POST" action="" enctype="multipart/form-data">