Django post_save() signal to register new user - django

Below is the createprofileview class-based view to register a new user and also to create the profile of that user at the same time.
class CreateProfileView(CreateView):
model = Profile
def post(self, request):
user_form = UserForm(request.POST)
profile_form = ProfileForm(request.POST, request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
return HttpResponseRedirect(reverse('home', args=[]))
messages.warning(request, 'Something went wrong in Venter, please try again')
def get(self, request):
user_form = UserForm()
profile_form = ProfileForm()
return render(request, './mysite/registration.html', {'user_form': user_form, 'profile_form': profile_form})
I have used post_save() signal along with my Profile model as follows:
class Profile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
primary_key = True
)
organisation_name = models.ForeignKey(
Organisation,
on_delete= models.CASCADE,
null=True,
)
profile_picture = models.ImageField(
upload_to='Organisation/Employee Profile Picture/%Y/%m/%d/',
null=True,
blank=True,
)
phone_number = models.CharField(
blank=True,
max_length=10
)
def __str__(self):
return self.user.username
#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()
However, the user gets created and saved, but its profile details do not get saved.
I am unsure how to tweak the signals function suitable to my requirements of registering a new user and also creating their profile at the same time.
Here is the forms.py:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'password', 'email', 'first_name', 'last_name')
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('organisation_name', 'phone_number', 'profile_picture')

I don't see the point of having a profile creation signal, because you are already having a ProfileForm to get the Profile Data. I think you can get rid of the profile signals and update the view to save the profile form directly. Like this:
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
# rest of the code
Update
from comments
You need to set your password in User object. You can do that in your form's save method by overriding like this(using set_password method):
class UserForm(...):
...
def save(self, **kwargs):
user = super(UserForm, self).save(commit=False)
password = self.cleaned_data.get('password')
user.set_password(password)
user.save()
return user

Related

How can i save custom fields in registration from to my database? (Django)

I'm trying to create a school related website with django. So i created a custom user model looks like this (Sorry for the foreign field names):
class ogrenciler(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
ogr_adi = models.CharField(max_length=30)
ogr_soyadi = models.CharField(max_length=30)
sinifi = models.ForeignKey(siniflar, null=True, on_delete=models.CASCADE)
numara = models.CharField(max_length=5)
foto = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return self.ogr_adi + " " + self.ogr_soyadi + "ogrencisi"
and this is my custom registiration form:
class ogrenciKayit(UserCreationForm):
email = forms.EmailField()
numara = forms.CharField(max_length=5, required=True)
class Meta:
model = User
fields = ['username', 'email', 'numara', 'password1', 'password2']
def save(self, commit=True):
user = super(ogrenciKayit, self).save(commit=False)
user.numara = self.cleaned_data['numara']
if commit:
user.save()
return user
I created a signal.py file to whenever a user created also create a "ogrenci"(student). This is my signals.py file:
#receiver(post_save, sender=User)
def create_ogrenci(sender, instance, created, **kwargs):
if created:
ogrenciler.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_ogrenci(sender, instance, **kwargs):
instance.ogrenciler.save()
Everything works fine but the "numara" field doesn't get saved.
This is my views.py file if you wanna check it:
def ogrenciregister(request):
if request.method == 'POST':
form = ogrenciKayit(request.POST)
if form.is_valid():
form.save()
username= form.cleaned_data.get('username')
messages.success(request, f'Hesap {username} adına oluşturuldu.')
return redirect('giris')
else:
form = ogrenciKayit()
return render(request, 'kullanicilar/ogrencikayit.html', {'form': form})
First, explain why the field can not be saved.
#receiver(post_save, sender=User)
The sender is just the user in the database, It is not the object you returned in your save function. It don not have the field you want
write the creation of related object in your save function of form .
In this way you can get the value of field

Django: can not create multiple model objects with one-to-one relation with one common model

I have two models that extend auth.models.User of django with one-to-one relation between model and the user. I want to create objects of either one of those two, using a form.
two models are :
class Worker(models.Model):
address = models.CharField(max_length=400)
user = models.OneToOneField(User, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_user_worker(sender, instance, created, **kwargs):
if created:
Worker.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_worker(sender, instance, **kwargs):
instance.worker.save()
class Employer(models.Model):
address = models.CharField(max_length=400)
user = models.OneToOneField(User, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_user_employer(sender, instance, created, **kwargs):
if created:
Employer.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_employer(sender, instance, **kwargs):
instance.employer.save()
And I have a creator view function as:
def worker_sign_up(request):
if request.method == 'POST':
form = WorkerSignUpForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.employer = None
user.worker = Worker()
user.worker.address= form.cleaned_data.get('address')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('home')
else:
form = WorkerSignUpForm()
return render(request, 'registration/workersignup.html', {'form': form})
I expect to have a user and a worker in my database, but It creates both Worker and Employer objects.
p.s.
My form class is:
class WorkerSignUpForm(UserCreationForm):
address = forms.CharField(max_length=400)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2')
This because you are associating a Employer object and saving the User. Instead of using user.save(), use user.worker.save(). This won't create any object for Employer.
#views.py
def worker_sign_up(request):
if request.method == 'POST':
form = WorkerSignUpForm(request.POST)
if form.is_valid():
user = form.save() #save the user
user.worker = Collector() # not sure why you need this
user.worker.address= form.cleaned_data.get('address')
user.worker.save() # this will create an object for Worker only
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('home')
else:
form = WorkerSignUpForm()
return render(request, 'registration/workersignup.html', {'form': form})
Also, you need to make the OneToOne field as optional.
# models.py
class Worker(models.Model):
address = models.CharField(max_length=400)
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True
)
class Employer(models.Model):
address = models.CharField(max_length=400)
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True
)

how to give a user a model field to choose from during signup?

how to make a user registration form including a model field in it as a required field?
like i want college field to show up on registration page as a drop-down menu
my models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
college = models.ForeignKey(College, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
class College(models.Model):
col = models.CharField(max_length=100)
def __str__(self):
return self.col
my forms.py
class UserForm(UserCreationForm):
col = College.objects.all()
password1 = forms.CharField(widget=forms.PasswordInput)
password2 = forms.CharField(widget=forms.PasswordInput)
college = forms.ChoiceField(widget=forms.Select(choices=col))
class Meta:
model = User
fields = ('username', 'college', 'password1', 'password2',)
and the views.py includes this:
def signup(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('home')
else:
form = UserForm()
return render(request, 'registration_form.html', {'form': form})
UPDATE
got to know about ModelChoiceField and made these changes in forms.py:
class UserForm(UserCreationForm):
password1 = forms.CharField(widget=forms.PasswordInput)
password2 = forms.CharField(widget=forms.PasswordInput)
college = forms.ModelChoiceField(queryset=College.objects.all(), empty_label=None)
class Meta:
model = User
fields = ('username', 'college', 'password1', 'password2')
but the college chosen is not being saved in the profile
also the admin section is giving an RelatedObjectDoesNotExist at /admin/login/ error

Issue with extending django model

I have a site where you can sign up to be either someone who uses the service(customer) or someone who provides the service(worker). I have created two profiles in models.py to represent each. They are both extremely similar for the most part as of right now. Both forms display properly when you go to them, and if you are signing up as a customer and press submit everything goes smoothly and a new user under will show up in "Customer profiles" at http://127.0.0.1:8000/admin/ . But if you try to sign up as a worker, the following error appears:
Exception Type: RelatedObjectDoesNotExist
Exception Value:
User has no workerprofile.
I do not understand this because as you will see in the code below i use customerprofile and it works fine, if I use workerprofile it crashes.
Views.py:
def signup_as_worker(request):
if request.method == 'POST':
form = WorkerSignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.workerprofile.birth_date = form.cleaned_data.get('birth_date')
user.workerprofile.university = form.cleaned_data.get('university')
user.save() # explicitly save custom fields not in User model
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user) # login user after signup
return redirect('home')
else:
form = WorkerSignUpForm()
return render(request, 'core/signup_as_worker.html', {'form': form})
def signup_as_customer(request):
if request.method == 'POST':
form = CustomerSignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.customerprofile.birth_date = form.cleaned_data.get('birth_date')
user.customerprofile.university = form.cleaned_data.get('university')
user.save() # explicitly save custom fields not in User model
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user) # login user after signup
return redirect('home')
else:
form = CustomerSignUpForm()
return render(request, 'core/signup_as_customer.html', {'form': form})
forms.py:
class WorkerSignUpForm(UserCreationForm):
#birth_date and university fields need to be declared seperately because they are not apart of User:
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
university = forms.CharField()
class Meta:
model = User
fields = ('username',
'email',
'first_name',
'last_name',
'birth_date',
'university',
'password1',
'password2', )
class CustomerSignUpForm(UserCreationForm):
#birth_date and university fields need to be declared seperately because they are not apart of User:
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
university = forms.CharField()
class Meta:
model = User
fields = ('username',
'email',
'first_name',
'last_name',
'birth_date',
'university',
'password1',
'password2', )
models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class WorkerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
university = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
role = models.CharField(max_length = 10, default = 'USER')
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_worker_profile(sender, instance, created, **kwargs):
if created:
WorkerProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_worker_profile(sender, instance, **kwargs):
instance.workerprofile.save()
class CustomerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
university = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
role = models.CharField(max_length = 10, default = 'CUSTOMER')
needLaundryDone = models.BooleanField(default = False)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_worker_profile(sender, instance, created, **kwargs):
if created:
CustomerProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_worker_profile(sender, instance, **kwargs):
instance.customerprofile.save()
I do not understand what the problem is.
Your signal handler method names for both models are the same. You are practically redefining the methods therefore only the second set of methods are called. Rename your CustomerProfile handlers to create_customer_profile and save_customer_profile.

Django: Extended User Model Cant Save

I have extended the django user model with another model called profile:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
chosenCharity = models.ForeignKey('meta.Charity', db_column='chosenCharityid', related_name='user_chosenCharity')
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = 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()
When I try to create a new user within the view with the code below i get the following error "(1048, "Column 'chosenCharityid' cannot be null")":
#transaction.atomic
def register(request):
selectedTeams = StraightredTeam.objects.filter(Q(teamid=request.session['team1id']) | Q(teamid=request.session['team2id'])).order_by('teamname')
request.POST.get('currentCharities')
next_url = request.POST.get('next', request.GET.get('next', reverse('straightred.payment')))
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
I know when a user has already created I should be able to use:
user = User.objects.get(pk=user_id)
user.profile.chosenCharity = 12
user.save()
But I am unsure how to do this when creating the user. Any help would be appreciated.
Below is a copy of the registration form to help:
class RegistrationForm(BootstrapModelForm, UserCreationForm):
email_opt_in = forms.BooleanField(label='Receive DWAD e-mail updates', required=False)
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
# The default Django user model doesn't require these fields to be set
# but we do.
self.fields['email'].required = True
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email__iexact=email).exists():
raise ValidationError('There is already an account registered with this e-mail address.')
return email
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', 'username']
Charity Model:
class Charity(models.Model):
name = models.CharField(max_length=50, unique=True)
website = models.URLField()
enabled = models.BooleanField(default=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
verbose_name_plural = 'charities'
don't create the Profile object in post_save signal. You cannot access the required charity id in create_user_profile method. So remove that part of code.
instead save the profile object right after you save your user object in your register view like this:
#transaction.atomic
def register(request):
selectedTeams = StraightredTeam.objects.filter(Q(teamid=request.session['team1id']) | Q(teamid=request.session['team2id'])).order_by('teamname')
request.POST.get('currentCharities')
next_url = request.POST.get('next', request.GET.get('next', reverse('straightred.payment')))
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
charity_id = request.session['chosenCharityid']
# create profile object
Profile.objects.create(user=new_user, chosenCharity_id=charity_id)
EDIT:
I see that you are using another method save_user_profile to receive the post_save signal.
Don't use this either. It a round about way of doing a simple straight forward thing.
Using you own code sample:
user = User.objects.get(pk=user_id)
# here you are assigning a new charity id to the profile object
user.profile.chosenCharity = 12
# you save the user object on which nothing has changed
# instead you should save the profile object
user.save() # no need
user.profile.save() # direct and logical