Userena - Extending the Profile model further upto two different models - python

What i am trying to achieve is that i want to extend the profile model further to either teacher or student. In the signup form I added a choice field where user select whether he is teacher or student. Below is my model structure.
class Profile(UserenaLanguageBaseProfile):
""" Default profile """
GENDER_CHOICES = (
(1, _('Male')),
(2, _('Female')),
)
user = models.OneToOneField(User,
unique=True,
verbose_name=_('user'),
related_name='profile')
gender = models.PositiveSmallIntegerField(_('gender'),
choices=GENDER_CHOICES,
blank=True,
null=True)
class Teacher(Profile):
profile = models.OneToOneField(Profile,
unique=True,
verbose_name=_('profile'),
related_name='teacher')
home_address = models.CharField(_('home_address'), max_length=255, blank=True)
home_phone = models.CharField(_('home_phone'), max_length=30, blank=True)
cell_phone = models.CharField(_('cell_phone'), max_length=30, blank=True)
experience = models.IntegerField(default = 0)
summary = models.TextField(_('summary'), max_length=500, blank=True)
class Student(Profile):
profile = models.OneToOneField(Profile,
unique=True,
verbose_name=_('profile'),
related_name='student')
grade = models.CharField(_('grade'), max_length=50, blank=True)
I am overriding the signup save method as:
def save(self):
new_user = super(SignupFormExtra, self).save()
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.save()
if self.cleaned_data['teacher_or_student'] == 'teacher':
teacher = Teacher(profile = new_user.get_profile())
teacher.save()
elif self.cleaned_data['teacher_or_student'] == 'student':
student = Student(profile = new_user.get_profile())
student.save()
return new_user
When teacher.save() or student.save() method is called it raises an integrity error that "(1048, "Column 'user_id' cannot be null")" but i am not creating a new user instance here i am trying to assign the newly created profile_id to teacher or student model. I am doing in the wrong way?? what should I do?

As the error says you can't create a Student or Teacher without user as you've defined it as a non nullable field.
Make sure you're passing your class the new_user you've defined..
# ...
new_user.save()
if self.cleaned_data['teacher_or_student'] == 'teacher':
teacher = Teacher(profile = new_user.get_profile(), user=new_user)
teacher.save()
elif self.cleaned_data['teacher_or_student'] == 'student':
student = Student(profile = new_user.get_profile(), user=new_user)
student.save()

I might be wrong, but why do you subclass your models from Profile model (so you have a "user" field in it already), and right after you have a "profile" OneToOneField field for Profile model again?

Related

Can we set only a specific type of user as a foreign key in a model's field

I have a model called CustomUser which holds two types of user, teachers and students. The model has a regd_as field which stores the type of user. The model looks as follows:
class CustomUser(AbstractBaseUser, PermissionsMixin):
REGD_AS_CHOICES = (
('TCH', 'TEACHER'),
('STU', 'STUDENT'),
)
id = models.UUIDField(primary_key = True, editable = False, default = uuid.uuid4)
email = models.EmailField(unique=True)
date_joined = models.DateTimeField(auto_now_add = True)
regd_as = models.CharField(max_length= 10, choices= REGD_AS_CHOICES, null=True)
is_staff = models.BooleanField(default=False)
is_active = models.Boolea
Now I want to have a model that would store a referal code generated only for users who are teachers. The model looks as follows:
class ReferalCode(models.Model):
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
teacher_user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name='teacher_user_referal', null=True)
referal_code = models.CharField(max_length=100, unique=True,null=True, blank=True)
Question
Is there a way to set only teachers from the **CustomUser** model as the foreignkey to teacher_user directly?
Currently I am using signals to store only teachers as the user in ReferalCode model.

Django AbstractBaseUser MultipleChoiceField

I have created a custom user model with AbstractBaseUser like this:
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
preference = models.CharField(max_length=400)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'preference']
objects = CustomUserManager()
def __str__(self):
return self.email
For the preference field i don't want a char field, but to create a MultipleChoiceField and when a user sign up to choose what he/she wants from the choices. Is there a simple way to do this?
I have a similar form which i used for different purpose:
class CategoryForm(forms.Form):
def __init__(self, *args, **kwargs):
categories = kwargs.pop("Category")
super(CategoryForm, self).__init__(*args, **kwargs)
self.fields["Category"] = forms.MultipleChoiceField(choices=categories, label='Click this to select categories')
EDIT
class CustomUser(AbstractBaseUser, PermissionsMixin):
category_list = pickle.load(open("/home/anonymous/Documents/Diploma-Recommender/Recommendation/Dataset"
"/category_list.pickle", "rb"))
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
category_tuple = ((),)
for j in category_list:
category_tuple = category_tuple + ((j, j),)
category_tuple = list(category_tuple)
category_tuple.pop(0)
category_tuple = tuple(category_tuple)
preference = ArrayField(
models.CharField(choices=category_tuple, null=True, max_length=50, blank=True, default=None),
)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'preference']
objects = CustomUserManager()
def __str__(self):
return self.email
I would recommend using an ArrayField in your model if you are using postgreSQL (which you should be!). This will accomplish your goal of multiple choices in a model field.
from django.contrib.postgres.fields import ArrayField
class CustomUser(AbstractBaseUser, PermissionsMixin):
CATEGORY_1 = 'Category_1'
CATEGORY_2 = 'Category_2'
CATEGORY_3 = 'Category_3'
CATEGORY_TYPE_CHOICES = [
(CATEGORY_1, 'Category_1'),
(CATEGORY_2, 'Category_2'),
(CATEGORY_3, 'Category_3')
]
category = ArrayField(
max_length=10,
choices=CATEGORY_TYPE_CHOICES,
null=True,
default=None,
blank=True
)
Read more here: Django Model MultipleChoice
if your multiple choice is sth like active diactive
you can just specify those like this :
CHOICES = (('one','one'), ('two','two') ...)
preference = models.CharField(max_length=400, choices = CHOICES)
this works if you want to get the gender or some limited choices , but if your choices is from different model like category and there is a model Category if the number of choices is 1 ForeignKey and if it is more than 1 use ManyToMany relation in your User model
and if you use ModelForm django handles it for you.

django model form saving data to multiple models

I have a Custom User model with a team field. I also have a Team model with a ManyToManyField for the Custom Users.
I have a template that allows a user to create a team using whatever Team Name they want. Upon creation, I want that user's Team to be assigned but the team primary key isn't created until the Team is created so how do I get that primary key id into the Custom User model?
My direction of thought is that I would perform a user save after the "create-team" form save in the views, but I'm not sure how to do that.
models.py
class Team(models.Model):
team_id = models.BigAutoField(auto_created=True, primary_key=True)
team_name = models.CharField(max_length=35, null=False, default='YourTeam')
team_type = models.CharField(choices=MEMBERSHIP_CHOICES, default='Free', max_length=30)
num_users = models.IntegerField(default=1)
emails = models.ManyToManyField('CustomUser', related_name='teamemails')
class CustomUser(AbstractUser):
username = None
first_name = models.CharField(max_length=255, unique=False, verbose_name='first name')
last_name = models.CharField(max_length=255, unique=False, verbose_name='last name')
email = models.EmailField(max_length=255, unique=True)
team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True, blank=True, related_name='userteam')
team_leader = models.BooleanField(default=False)
team_member = models.BooleanField(default=False)
forms.py
class CreateTeamForm(forms.ModelForm):
team_name = forms.CharField(label='Team Name', required=True)
class Meta:
model = Team
fields = ['team_name']
views.py
#verified_email_required
def create_team(request):
template = 'users/create_team.html'
if request.method == "POST":
form = CreateTeamForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.team_name = request.team_name
form.save()
messages.success(request, 'Team Created!')
return redirect('home')
else:
messages.error(request, 'Oops! Something went wrong')
else:
form = CreateTeamForm()
context = {
'form':form,
}
return render(request, template, context)

Model field to automatically store total club members

I am building an app for a charity club that has many different users each belonging to a single club. I want to automatically increment the 'total_members' field of the class 'Club_Chapter' each time a user registers their account for a particular school (hence the User foreign key 'chapter').
models.py
class Club_Chapter(models.Model):
club_id = models.IntegerField(primary_key=True)
school_name = models.CharField(max_length=30)
state = models.CharField(max_length=4)
total_members = models.IntegerField(null = False, default = 0)
def __str__(self):
return self.school_name
# User Model
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
chapter = models.ForeignKey('Club_Chapter',on_delete=models.PROTECT)
ranking = models.CharField(default='member', max_length=20)
REQUIRED_FIELDS = []
objects = UserManager()
forms.py
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=True)
last_name = forms.CharField(max_length=30, required=True)
email = forms.EmailField(max_length=254, required=True)
chapter = models.ForeignKey('Club_Chapter',on_delete=models.PROTECT)
password2 = None
# below is my failed attempt to solve this problem
Club_Chapter.objects.update(total_members=F('total_members') + 1)
class Meta:
model = User
fields = ('first_name', 'last_name',
'chapter','email', 'password1',)
I know this seams like a trivial problem but I have been searching everywhere with no luck. Any input will be greatly appreciated for the solution to this problem will help greatly with other aspects of this project.
I'm not sure what it's important to have an auto-incrementing total_members fields. Django has super powerful querying capabilities. For instance, this would work perfectly:
total_members = User.objects.filter(chapter__school_name="My Cool School").count()
Notice I used the double underscore after chapter to reference a particular field in the foreign key.
Hope this helps.

Django/Views executing two forms

I'm really having some trouble with this. I've got some custom user's setup and those users can be attached to companies via foreign key. I'm just having trouble saving them. I've tried a ton of different variations of getting the user attached to a company and I just can't crack it. The forms do work and it does both create a "customer" and a "customer company".
I know this needs to be a variation of:
if customer_form.is_valid() and customer_company_form.is_valid():
customer_company = customer_company_form.save()
customer = customer_form.save(commit=False)
customer.user = customer_company
customer_company.save()
models.py
class CustomerCompany(models.Model):
COUNTRIES = (
('USA', 'United States'),
('CAN', 'Canada')
)
name = models.CharField(max_length=100, blank=True, unique=True)
website = models.CharField(max_length=100, blank=True)
phone = models.CharField(max_length=10, blank=True)
address = models.CharField(max_length=100, blank=True)
city = models.CharField(max_length=255, blank=True)
state = USStateField(blank=True, null=True)
us_zipcode = USZipCodeField(blank=True, null=True)
ca_province = models.CharField(max_length=50, blank=True, null=True)
ca_postal_code = models.CharField(max_length=7, blank=True, null=True)
country =models.CharField(max_length=3, choices=COUNTRIES,
blank=True)
def get_absolute_url(self):
return reverse('accounts:customer_company_detail',kwargs={'pk':self.pk})
def __str__(self):
return self.name
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='customer_profile')
company = models.ForeignKey(CustomerCompany, on_delete=models.CASCADE, null=True)
phone = models.CharField(max_length=10)
def __str__(self):
return self.user.first_name + ' ' + self.user.last_name
forms.py
class CustomerSignupForm(UserCreationForm):
first_name = forms.CharField(max_length=50, required=True)
last_name = forms.CharField(max_length=50, required=True)
phone = forms.CharField(max_length=10, required=True)
email = forms.EmailField(required=True)
class Meta(UserCreationForm.Meta):
model = User
#transaction.atomic
def save(self, commit=True):
user = super(CustomerSignupForm, self).save(commit=False)
user.is_customer = True
user.first_name = self.cleaned_data.get('first_name')
user.last_name = self.cleaned_data.get('last_name')
user.email = self.cleaned_data.get('email')
user.save()
customer = Customer.objects.create(user=user)
customer.phone = self.cleaned_data.get('phone')
customer.save()
return user
class CustomerCompanyCreateForm(forms.ModelForm):
ca_province = CAProvinceField(required=False, label="Province")
ca_postal_code = CAPostalCodeField(required=False, label="Postal Code")
class Meta:
model = CustomerCompany
fields = ['name', 'website', 'phone', 'address', 'city', 'state',
'us_zipcode', 'country', 'ca_province', 'ca_postal_code']
labels = {
"us_zipcode": "Zipcode",
}
views.py Updated with working code
def customer_signup(request):
if request.method == 'POST':
customer_form = CustomerSignupForm(request.POST)
customer_company_form = CustomerCompanyCreateForm(request.POST)
if customer_form.is_valid() and customer_company_form.is_valid():
# first save the user object
user_obj = customer_form.save(commit=False)
# Then use this object to get to my Customer model via the related name
customer = user_obj.customer_profile
# now save the CustomerCompany
company = customer_company_form.save()
# attach the customer to the Company
customer.company = company
# now fully save the customer after he's attached to his company
customer.save()
return redirect('customer_dashboard:customer_dashboard')
else:
messages.error(request, 'Please correct the errors below.')
else:
customer_form = CustomerSignupForm()
customer_company_form = CustomerCompanyCreateForm()
return render(request, 'accounts/registration/customer_signup_combined.html', {
'customer_form' : customer_form,
'customer_company_form' : customer_company_form,
})
You're saving both forms in your view but you're not connecting the two objects.
Calling save on the customer_form will return a User object since its a User ModelForm. You can use this object to get to the Customer object via the customer_profile related_name and set the company field to the Company instance returned when you save the customer_company_form.
It should look like this:
if customer_form.is_valid() and customer_company_form.is_valid():
user_obj = customer_form.save(commit=True)
customer = user_obj.customer_profile
company = customer_company_form.save(commit=True)
customer.company = company
customer.save()
return redirect('customer_dashboard:customer_dashboard')

Categories

Resources