Issue extending django User model - django

I have a website where the user should be able to sign up as a "worker" or a "customer", an uber model type of site. I created the two models WorkerProfile and CustomerProfile and the two forms, but each time I submit either the customer or worker form, the new user gets put in both the Customer profile's and Worker profile's in the database at http://127.0.0.1:8000/admin/ , how do I prevent this from happening?
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_customer_profile(sender, instance, created, **kwargs):
if created:
CustomerProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_customer_profile(sender, instance, **kwargs):
instance.customerprofile.save()
forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
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', )
views.py:
def signup(request):
if request.method == 'POST':
form_worker = WorkerSignUpForm(request.POST)
form_customer = CustomerSignUpForm(request.POST)
if form_worker.is_valid():
user = form_worker.save()
user.refresh_from_db() # load the profile instance created by the signal
user.workerprofile.birth_date = form_worker.cleaned_data.get('birth_date')
user.workerprofile.university = form_worker.cleaned_data.get('university')
user.save() # explicitly save custom fields not in User model
raw_password = form_worker.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user) # login user after signup
return redirect('home')
elif form_customer.is_valid():
user = form_customer.save()
user.refresh_from_db() # load the profile instance created by the signal
user.customerprofile.birth_date = form_customer.cleaned_data.get('birth_date')
user.customerprofile.university = form_customer.cleaned_data.get('university')
user.save() # explicitly save custom fields not in User model
raw_password = form_customer.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user) # login user after signup
return redirect('home')
else:
form_worker = WorkerSignUpForm(request.POST)
form_customer = CustomerSignUpForm(request.POST)
return render(request, 'core/signup.html', {'form_worker': form_worker,'form_customer': form_customer })
signup.html:
{% extends 'core/base.html' %}
{% block head %}
<title> Sign Up</title>
{% endblock %}
{% block body %}
<h3>Sign Up As Worker</h3>
<form method="post">
{% csrf_token %}
{{ form_worker.as_p }}
<button type="submit">Sign up</button>
</form>
<h3>Sign Up As Customer</h3>
<form method="post">
{% csrf_token %}
{{ form_customer.as_p }}
<button type="submit">Sign up</button>
</form>
{% endblock %}

Don't use signals here. They both fire on save of User, and each create their related object.
You should remove those signals and instead do this in the view. In the is_valid block for each form you can create only the specific object you need.
if form_worker.is_valid():
user = form_worker.save()
worker = WorkerProfile(user=user)
worker.birth_date = form_worker.cleaned_data.get('birth_date')
worker.university = form_worker.cleaned_data.get('university')
worker.save()
raw_password = form_worker.cleaned_data.get('password1')
...
elif form_customer.is_valid():
user = form_customer.save()
customer = CustomerProfile(user=user)
...

This is normal :
user = models.OneToOneField(User, on_delete=models.CASCADE)
If you call this in both models, so during the save(), it will create both.
What I propose you, is to modify your models. Why don't you just create a model 'Profil' and put a boolean field 'is_customer' True/False ?

Related

Access data of a model linked through a OneToOneField model on a form

I want a pre-populated form with the details (e.g. first name and surname) about the profile of a logged-in user, so that they can update them. I have a custom user model with first name and surname in it, and then a profile model which is linked to the user model and extends it, containing some extra information.
I've defined a constant within the profile model which theoretically should get the user's first name and surname.
models.py:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name="email", unique=True, max_length=255)
first_name = models.CharField(max_length=30, blank=True, null=True)
surname = models.CharField(max_length=30, blank=True, null=True)
[...]
objects = UserManager()
[...]
Profile model added
class Profile(models.Model):
user = models.OneToOneField(User, related_name='current_user', on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def surname(self):
return self.user.surname}
def first_name(self):
return self.user.first_name
[...]
views.py:
#login_required
def profile_edit(request):
if request.method == 'POST':
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
if p_form.is_valid():
p_form.save()
messages.success(request, f'Your account has been updated')
[...]
forms.py:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('first_name', 'surname')
template.html:
{% extends "base.html" %}
{% block content %}
<div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ p_form }}
<button class="button" type="submit"> User update</button>
</form>
</div>
{% endblock content %}
When accessing the template via the browser I expect to see the form already populated with the profile's (i.e. user's) first name and surname. Instead, I get a django.core.exceptions.FieldError: Unknown field(s) (surname, first_name) specified for Profile in the shell.
--
Answer
User ruddra's answer works fine, and I've flagged it as the answer to my problem. Nevertheless, declaring two different form objects and printing them out in the template would also work:
views.py:
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
forms.py:
class UserUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'surname')
class ProfileUpdateFormOld(forms.ModelForm):
class Meta:
model = Profile
fields = ('image',)
template.html:
{{ u_form }}
{{ p_form }}
Basically those fields are from User model, they are not in Profile model. So you can change the model class in ProfileUpdateForm to User:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'surname')
Updated answer based on comments:
class ProfileUpdateForm(forms.ModelForm):
first_name = forms.CharField(max_length=255)
surname = forms.CharField(max_length=255)
def __init__(self, *args, **kwargs):
super(ProfileUpdateForm, self).__init__(*args, **kwargs)
self.initial['first_name'] = self.instance.first_name()
self.initial['surname'] = self.instance.surname()
class Meta:
model = Profile
fields = ('first_name', 'surname')
def save(self, *args, **kwargs):
user = self.instance.user
user.first_name = self.cleaned_data.get('first_name')
user.surname = self.cleaned_data.get('surname')
user.save()
return super(ProfileUpdateForm, self).save(*args, **kwargs)
Alternative to override the __init__(...) method is to send the initial data when initiating the form, pass the initial data. For example:
profile = request.user.profile
ProfileUpdateForm(instance=profile, initial={'first_name':profile.first_name(), 'surname': profile.surname()})

Prepopulate ModelMultipleChoiceField Django

I am creating a website that allows users to follow stocks and see articles based on those stocks. Upon registration the user follows Stocks for the first time. After this I would like them to be able to view a page that shows all Stocks and which ones they follow. How can I prepopulate a ModelMultipleChoiceField?
models.py:
class Stock(models.Model):
name = models.CharField(max_length = 50)
ticker = models.CharField(max_length = 50)
def __str__(self):
return self.name
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
followed_stocks = models.ManyToManyField(Stock, blank=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
views.py:
def test(request):
if request.method == "POST":
form = StockFollowForm(request.POST)
if form.is_valid():
request.user.profile.followed_stocks = list(form.cleaned_data.get('stocks_selected'))
request.user.profile.save()
return redirect('index')
else:
form = StockFollowForm() #how do I prepopulate this if there are already followed Stock objects
return render(request, 'core/test.html',{'form': form})
template:
<div class = "container">
<h2 class = "text-center">Register</h2>
<form method = 'post'>
{% csrf_token %}
{{ form }}
<div class = "text-center">
<br/>
<button class="btn btn-primary" type = 'submit'>Follow/Unfollow Stocks</button>
</div>
</form>
</div>
forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import Stock
from django.forms import ModelMultipleChoiceField
class ProfileRegistrationForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'password1', 'password2', 'email', 'first_name' ,'last_name')
class StockFollowForm(forms.Form):
stocks = forms.ModelMultipleChoiceField(required =False,
widget=forms.CheckboxSelectMultiple,
queryset=Stock.objects.all())
Try specifying the initial value for the stocks field:
form = StockFollowForm(
initial={'stocks': request.user.profile.followed_stocks.all()}
)
For more on this, check out the Django docs on providing initial values to a ModelForm

Django Forms: Cannot get to data to save from ModelForm

I am creating a job board site. Right now I can successfully register an Employer but when I try to create a job listing while logged in as an Employer, the data from the form does not save to the database. I have the following 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
from django.http import HttpResponse
# Create your models here.
class Employer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.first_name
#receiver(post_save, sender=User)
def create_employer(sender, instance, created, **kwargs):
if created:
Employer.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_employer(sender, instance, **kwargs):
instance.employer.save()
class Job(models.Model):
poster = models.ForeignKey(Employer, on_delete=models.CASCADE)
job_title = models.CharField(max_length=50)
establishment_name = models.CharField(max_length = 50)
details = models.TextField(max_length = 2000)
salary = models.CharField(max_length = 20)
address = models.CharField(max_length = 50)
state = models.CharField(max_length = 20)
zip_code = models.CharField(max_length = 10)
def __str__(self):
return self.job_title + " - " + self.establishment_name \
+ ", " + self.poster.user.first_name + " " +self.poster.user.last_name
A user can register as an employer just fine, but I am having problems getting Jobs to save to the database. Once a user registers/logs in as an employer they are redirected to employer_home.html, where an employer can post a job:
{% extends 'core/base.html' %}
{% block body %}
<h1>Post a Job</h1>
<form>
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Post</button>
</form>
{% endblock %}
Here is my forms.py:
from django.forms import ModelForm
from .models import Job
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class EmployerSignupForm(UserCreationForm):
class Meta:
model = User
fields = ('first_name',
'last_name',
'email',
'username',
'password1',
'password2',)
class JobPostForm(ModelForm):
class Meta:
model = Job
fields= ('job_title',
'establishment_name',
'salary',
'address',
'state',
'zip_code',
)
and here is my employer_view(view to handle Job form):
def employer_home(request):
if request.method == 'POST':
form = JobPostForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('Working!')
else:
form = JobPostForm()
return render(request, 'core/employer_home.html', {'form': form})
employer_home.html displays a form with no problem, but when the form is submitted none of the data is saved to the database and the return HttpResponse('Working!') is never executed, it simply reloads the empty form. Does anyone know how to fix this?
Add method="POST" in your form. In your view do this:
def employer_home(request):
if request.method == 'POST':
form = JobPostForm(request.POST)
if form.is_valid():
job_object = form.save(commit=False)
job_object.poster = poster_object
job_object.save()
return HttpResponse('Working!')
else:
form = JobPostForm()
return render(request, 'core/employer_home.html', {'form': form})
A good example is shown here: example
Try <form method="post"> in your html template.
By default, the method is get.

Creating a User Profile page using OneToOne field with User Model

I'm currently using Django all-auth, and it has a /accounts/profile page which I want to create/populate with a form which updates user information.
I have a Teacher field, which extends the User Model using OneToOne field.
models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='Teacher')
bio = models.TextField(max_length=500, blank=True)
availability = models.BooleanField(default=False)
teacher_logo = models.FileField()
This teacher model is what I want the user to update in /accounts/profile.
forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class TeacherForm(forms.ModelForm):
class Meta:
model = Teacher
fields = ('availability', 'bio','teacher_logo')
views.py
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
teacher_form = TeacherForm(request.POST, instance=request.user.teacher)
if user_form.is_valid() and teacher_form.is_valid():
user_form.save()
teacher_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('users:index')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
teacher_form = TeacherForm(instance=request.user.teacher)
return render(request, 'accounts/profile.html', {
'user_form': user_form,
'teacher_form': teacher_form
})
template users/profile.html
<form method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ teacher_form.as_p }}
<button type="submit">Save changes</button>
</form>
urls.py
url(r'^profile/$', views.update_profile, name='Update-Profile')
I can use an update view, but then I need to specify in the URL, which seems an incorrect way of doing it; Also, users will be able to edit someone else profiles.
When I run the above, I get a complaint that 'User' object has no attribute 'teacher'.
When I remove .teacher from TeacherForm(instance=request.user.teacher) It loads the page with the form, but when I update, it still gives me the same complaint (removed in both places in views.py)
EDIT: models.py extra
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Teacher.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.Teacher.save()
you set related name as Teacher, so you need:
teacher_form = TeacherForm(instance=request.user.Teacher)
# ^^^^
or better set related_name to 'teacher'
class Teacher(models.Model):
user = models.OneToOneField(
User,
on_delete=models.PROTECT,
related_name='teacher')
# ^^^

How to save custom fields to DB in Django registrationform?

ive been struggling with this particular part for days now, and other posts on this problem dont seem to help me out.
So I have connected the User model with my own Gebruiker model that has fields like city, postalcode etc. Now I have managed to show them up on the registerform, but they dont seem to work. My second question is about the order of the inputfields, every time they appear in a random order. How can I set up a steady order?
This is my forms.py
from django import forms
from django.contrib.auth.models import User
from .models import Gebruiker
from django.contrib.auth.forms import UserCreationForm
class RegistrationForm(UserCreationForm):
woonplaats = forms.CharField(required=True)
postcode = forms.CharField(required=True)
class Meta:
model = User
fields = {'username','email', 'password1', 'password2', 'first_name', 'last_name', 'woonplaats', 'postcode'}
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
gebruiker = Gebruiker(user=user, woonplaats=self.cleaned_data['woonplaats'], postcode=self.cleaned_data['postcode'])
gebruiker.save()
if commit:
gebruiker.save()
user.save()
return user, gebruiker
(where woonplaats = place of residence)
Here is a part from views.py
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return redirect('/aanmelden')
else:
form = RegistrationForm()
args = {'form' : form}
return render(request, 'aanmelden/reg_form.html', args)
Models file:
from django.db import models
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
# Create your models here.
class Gebruiker(models.Model):
user = models.OneToOneField(User, null=True)
woonplaats = models.CharField(max_length=100)
postcode = models.CharField(max_length=100, default="")
email = models.CharField(max_length=100, default="")
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Gebruiker.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.gebruiker.save()
Gebruiker part in User:
!(http://imgur.com/RKVCbc2)
You have to create object Gebruiker. So, instead of this line:
gebruiker = Gebruiker(user = user, woonplaats = self.cleaned_data['woonplaats'], postcode = self.cleaned_data['postcode'])
gebruiker.save()
change to this:
gebruiker = Gebruiker.objects.create(user = user, woonplaats = self.cleaned_data['woonplaats'], postcode = self.cleaned_data['postcode'])
gebruiker.save()
if Gebruiker created already, you have to get this object:
gebruiker = Gebruiker.objects.get(your_parameter)
gebruiker = Gebruiker(user = user, woonplaats = self.cleaned_data['woonplaats'], postcode = self.cleaned_data['postcode'])
gebruiker.save()
And the second answer, about ordering of the fields. As I understand your template looks something like that:
{{form}}
but you can access to the different fields using this code:
<form action="" method="post">
...
{{ form.woonplaats }}
{{ form.postcode }}
...
<input type="submit" alt="register" value="Order" />
</form>
In this case you can change the order as you want
UPDATE:
gebruiker = Gebruiker.objects.get(user = user)
gebruiker.woonplaats = self.cleaned_data['woonplaats'],
gebruiker.postcode = self.cleaned_data['postcode'])
gebruiker.save()