Django: Extended User Model Cant Save - django

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

Related

How to Query a ForeignKey field pointing to another ForeignKey field in Django

I have a model (Letter) with a foreign key, pointing to another model (Company) with a foreign key. Below is a simple schema from models.py
from django.contrib.auth.models import User
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
date_created = models.DateField(default=timezone.now, null=True)
company_name = models.CharField(max_length=100, null=True)
class Letter(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='letterhead_user', null=True)
company = models.ForeignKey(Company, related_name = "company_letter", on_delete=models.CASCADE, null=True)
subject = models.CharField(max_length=5000, null=True)
body = models.TextField()
I have created a form where users can create Letters with the model through ModelForm.
class LetterForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(LetterForm, self).__init__(*args, **kwargs)
self.fields['company'].widget.attrs = {'class': 'input',}
self.fields['subject'].widget.attrs = {'class': 'input', 'placeholder': 'RE: ...'}
self.fields['body'].widget.attrs = {'class': 'textarea',}
class Meta:
model = Letter
fields = ('company', 'subject', 'body',)
The View:
def letter_form (request):
form = LetterForm()
if request.method == 'POST':
form = LetterForm(request.POST, request.FILES)
form.instance.user = request.user
if form.is_valid():
form.save()
return redirect('letter')
Currently, when the user is presented with a form to create a Letter, on the Company field, all the companies from all the users appear. See the pic below:
Front end Form
I would like only the companies that the User has created to appear in the drop-down, not all companies from all users. Or to be able to select the first company that the User has created.
You can specify the logged in user in the form and filter accordingly. In the constructor of the form we thus limit the queryset with:
class LetterForm(forms.ModelForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
self.fields['company'].widget.attrs = {'class': 'input',}
self.fields['subject'].widget.attrs = {'class': 'input', 'placeholder': 'RE: …'}
self.fields['body'].widget.attrs = {'class': 'textarea',}
if user is not None:
self.fields['company'].queryset = Company.objects.filter(user=user)
# …
and in the view, we then pass the logged in user to the constructor of the form:
from django.contrib.auth.decorators import login_required
#login_required
def letter_form(request):
if request.method == 'POST':
form = LetterForm(request.POST, request.FILES, user=request.user)
form.instance.user = request.user
if form.is_valid():
form.save()
return redirect('letter')
else:
form = LetterForm(user=request.user)
# …
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
If you use a ModelForm for Letterit may look like this:
class LetterForm(forms.ModelForm):
class Meta:
model = Letter
fields = ["company", "subject", "body"]
# you need to init the form with the right user instance
def __init__(self, user=None, *args, **kwargs):
# call the default __init__ behavior
super().__init__(*args, **kwargs)
# this is the trick, you will filter the companies queryset here
if user:
self.fields['company'].queryset = Company.objects.filter(user=user)
So you need to pass down the user in the form from your view:
something like:
def my_view(request):
# assuming the user follow the standard Django user implementation
# and you user is logged in
form = LetterForm(request.POST or None, user=request.user)
if form.is_valid():
form.save()
# then redirect the user to whatever success page
# render the form
return render(request, "your_template.html", {"form": form})

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
)

Django post_save() signal to register new user

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

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.