I have a User model and Employee model which stands for additional info about user, it has one to one field relation with User instance. When new User is created I use signals to create new Employee instance. Now I added new field to User model called 'fk_employee_id' just to have a link to this newly created Employee and I'm not sure how to do pass it's id to this User's fk field. I tried to write in my signals something like instance.user.fk_employee_id = sender after that I get
ValueError Cannot assign "<class 'employees.models.Employees'>": "User.fk_employee_id" must be a "Employees" instance.
So how do I fill this foreign key field in User instance when Employee is created?
My User model:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True, validators=[validate_username])
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=50)
fk_employee_id = models.OneToOneField('employees.Employees', related_name='fk_employee_id',
null=True,on_delete=models.DO_NOTHING)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
objects = UserManager()
The Employee model is large, it has basic fields like number, address etc. Nothing special.
My signals file in users app:
from django.db.models.signals import post_save
from .models import User
from django.dispatch import receiver
from employees.models import Employees
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Employees.objects.create(user=instance, first_name=instance.first_name,
last_name=instance.last_name)
#receiver(post_save, sender=Employees)
def save_profile(sender, instance, **kwargs):
instance.user.save()
You don't need 2 OneToOneFields.
One for the Employee is enough.
Let's say you have:
class Employee(models.Model):
user = models.OneToOneField(User, related_name='employee_profile',on_delete=models.DO_NOTHING)
...
After migration, you can just call user.employee_profile (like in the related_name attribute).
Related
I am trying to create multi user registration system with Django. However, anytime I call the save() method to save a User type, it saves into the User table twice. The funny thing about the second model that is saved is that many required fields are empty.
I am using a custom user model that I created from AbstractBaseUser. I also rewrote the forms for the CustomUser model. For the multiple user types, I am using a profile model (Student model has a OneToOne field to the user model)
models.py:
class User(AbstractBaseUser, PermissionsMixin):
# I've removed some unimportant code here
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
class Types(models.TextChoices):
STUDENT = 'STUDENT', 'Student'
DEPARTMENT_USER = 'DEPARTMENT_USER', 'Department user'
ADMIN = 'ADMIN', 'Admin'
user_type = models.CharField(_('Type'), max_length=50, choices=Types.choices, default=Types.STUDENT)
first_name = models.CharField(_('First name'), max_length=70, blank=False, default="")
middle_name = models.CharField(_('Middle name'), max_length=70, blank=True, default="")
last_name = models.CharField(_('Last name'), max_length=70, blank=False, default="")
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False) # a admin user; non super-user
is_superuser = models.BooleanField(default=False) # a superuser
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['user_type', 'first_name', 'last_name'] # Email & Password are required by default.
objects = UserManager()
class Meta:
verbose_name = ('user')
verbose_name_plural = ('users')
#db_table = 'auth_user'
abstract = False
class AccountConfirmed(models.Model):
# Model to determine which users have confirmed their email addresses.
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='accountconfirmed')
email_confirmed = models.BooleanField(default=False)
reset_password = models.BooleanField(default=False)
class Meta:
app_label = 'auth'
# When the user model is created, through signals an AccountConfirmed model is also created.
# The email_confirmed and reset_password field is set to false.
#receiver(models.signals.post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
AccountConfirmed.objects.create(user=instance)
instance.accountconfirmed.save()
######################################################
######################################################
class Student(User):
# This is the model class for students
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='students')
matric_number = models.CharField(_('Matriculation number'), max_length=11, blank=False)
department = models.CharField(_('Department'), max_length=40, blank=False)
# has_graduated, level, etc. future possibilities
def __str__(self):
return f'{self.user.email}'
forms.py:
class StudentSignupForm(UserCreationForm):
# first_name = forms.CharField(max_length=70)
# middle_name = forms.CharField(max_length=70, required=False)
# last_name = forms.CharField(max_length=70)
matric_number = forms.CharField(min_length=10, max_length=11, help_text='Your Matric number must be 10 characters')
department = forms.CharField(max_length=40, help_text='e.g Computer Science')
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('matric_number', 'department')
#transaction.atomic
def save(self, commit=True):
# Save the User instance and get a reference to it
user = super().save(commit=False)
user.user_type = User.Types.STUDENT
user.is_active = False
#if commit:
user.save()
print(f' forms.py {user.email} {user.first_name}')
student = Student.objects.create(user=user, matric_number=self.cleaned_data.get('matric_number'), department=self.cleaned_data.get('department'))
# Add other details
# Return User instance, not Student instance
return user
views.py:
class StudentUserSignupView(CreateView):
model = User
template_name = 'account/signup.html'
form_class = StudentSignupForm
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'STUDENT'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
#login(self.request, user)
send_verification_mail(self, user)
return redirect('verification_sent')
Anytime a user signs up, this is what the students table looks like:
Also, this is what the users table look like after signup (with the multiple saves)
So how do I correct the multiple saves in the user table?
Also, How is it even possible to save a model with most of the required fields empty?
As pointed out by #RaghavKundra, the line below was what caused the problem of saving multiple times to the database
class Student(User):
Instead of that, it should be
class Student(models.Model):
I have two class model, the User and Money Request. I am trying to access the data I entered in the User class so that whenever I requested money using the MoneyRequest class, I can also input my entered email, first and last name together with the withdraw_money.
I really need the data from the User class so that whenever I look at the admin page, I can see the name of the user who sent the money request.
Here is my models.py
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=100, unique=True)
first_name = models.CharField(max_length=100, blank=True, null=True)
last_name = models.CharField(max_length=100, blank=True, null=True)
class MoneyRequest(models.Model):
date_requested = models.DateTimeField(auto_now_add=True)
withdraw_money = models.DecimalField(null=True, blank=True, max_digits=8, decimal_places=2, help_text='Minimum withdrawal is ₱300.00.', validators=[minimum_money])
Here is my views for my MoneyRequest class
class UserAccountsView(CreateView):
model = MoneyRequest
fields = ['withdraw_money',] # Keep listing whatever fields
template_name = 'users/accounts.html'
def form_valid(self, form):
user = form.save()
user.save()
return redirect('users:user_account', self.request.user.username)
What should I put in my MoneyRequest class in order to get the data entered in the User class? Thank you!
In your moneyrequest model add
user = models.ForeignKey(User, on_delete=models.CASCADE)
Then you can get all User model data.
I have user model using AbstractBaseUser class. I am registering user using email, username and password.
User class:
class User(AbstractBaseUser):
"""
Creates a customized database table for user
"""
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
username = models.CharField(max_length=255, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email',]
objects = UserManager()
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
I want to extend this model to another model called Employee. For extending user model to employee model I have used django signal class.
Employee class:
class Employee(models.Model):
"""
Create employee attributes
"""
employee_user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True)
e_id = models.IntegerField(unique=True, null=True, blank=True)
first_name = models.CharField(max_length=128,blank=True)
last_name = models.CharField(max_length=128, blank=True)
address = models.CharField(max_length=256, blank=True)
phone_number = models.CharField(max_length=128, blank=True)
email = models.EmailField(max_length=128, unique=True)
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Employee.objects.create(employee_user=instance)
instance.Employee.save()
In this model there is also a field named email. I have to keep this field because I am using this model to create a form for Employee creation. But the problem is, User model's email field is conflicting with employee email field. I have to keep both the email fields.
Can I update the employee email field with user email field?? If yes, then what should be the query???
You can do Employee.objects.create(employee_user=instance, email=instance.email), but you should evaluate if you need to repeat the data in two tables. You can always access employee.employee_user.email whenever you have an Employee instance.
I have a field called org model called ScrummyUser with a one to one field with a model called User which inherits from AbstractUser model, how do I save data from the form field into the scrummyuser model
This is the Organization Model
class Organization(models.Model):
organization = models.CharField(max_length=255, null=True)
def __str__(self):
return self.organization
This is the ScrummyUser model
class ScrummyUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='scrummy_profile')
role= models.CharField(max_length=100, choices=role, blank=True, null=True, default='DEV')
org = models.ForeignKey(Organization, max_length=255, on_delete=models.CASCADE, default=1)
This is the ScrummySignup form
class ScrummySignUpForm(UserCreationForm):
role = forms.ChoiceField(choices=role, required=False)
org = forms.ModelChoiceField(
queryset=Organization.objects.all(),
widget=forms.Select
)
class Meta(UserCreationForm.Meta):
model = User
fields = ['first_name', 'last_name','username' ,'email']
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_user = True
user.save()
scrummy = ScrummyUser.objects.create(user=user, role=role)
return user
It throws me this errormessage
scrummy = ScrummyUser.objects.create(user=user, role=role, org=org)
NameError: name 'org' is not defined
which is obvious but I am looking for another approach to save the data
for access CutomUser with Abstract you should get User of settings:
from django.conf import settings
class ScrummyUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
primary_key=True, related_name='scrummy_profile')
AbstractUser link
ok so I figured this out, after defining a variable org as a modelchoicefield, i passed it unto the fields for user model as a user form field before saving it into the scrummyuser model.
I know on this topic people asked a question before but my case is different and I have implemented almost all the solutions which I found but none of them are worked for me.
First I have defined three classes in models:
models.py
class User(AbstractBaseUser, PermissionsMixin):
""" User Model """
username = None
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
agency = models.ForeignKey('agency.Agency', on_delete=models.CASCADE, null=True)
weekly_email = models.NullBooleanField()
is_create_new_password = models.NullBooleanField(default=True)
is_active = models.BooleanField(default=True)
last_login_time = models.DateTimeField(null=True)
last_login_from = models.CharField(max_length=255, null=True)
created_at = models.DateField(default=timezone.now)
updated_at = models.DateField(default=timezone.now)
created_by = models.IntegerField(null=True)
updated_by = models.IntegerField(null=True)
""" The `USERNAME_FIELD` property tells us which field we will use to log in.
In this case, we want that to be the email field. """
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
""" Tells Django that the UserManager class defined above should manage
objects of this type. """
objects = UserManager()
class Role(models.Model):
""" Role Model """
name = models.CharField(max_length=255, unique=True)
class UserRole(models.Model):
""" User Role Model """
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
Then I have defined my serializer for user module:
serializers.py
class RegistrationSerializer(serializers.ModelSerializer):
""" Serializers registration requests and creates a new user. """
user_id = serializers.IntegerField(required=False)
email = serializers.EmailField(max_length=255)
name = serializers.CharField(max_length=255)
agency_id = serializers.IntegerField(source='agency.id', required=False)
role = serializers.CharField(source='role.name')
weekly_email = serializers.NullBooleanField()
last_login_time = serializers.DateTimeField(required=False)
last_login_from = serializers.CharField(max_length=255, required=False)
class Meta:
model = User
fields = (
'role', 'user_id', 'email', 'name', 'agency_id', 'weekly_email', 'last_login_time',
'last_login_from'
)
And At the end, I have defined my view file for user creation:
views.py
class UserCreateAPIView(APIView):
""" User create Api view class """
#Allow any user (authenticated or not) to hit this endpoint.
permission_classes = (IsAuthenticated,)
serializer_class = RegistrationSerializer
def post(self, request):
""" create user using following logic. """
request.data['user_id'] = request.user.id
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user)
return Response({'message': response['user']['created'], 'data': serializer.data},
status=status.HTTP_201_CREATED)
Now when I run it everything works fine like user is created, role is created as per my expectations. My view, serializer and models excuted but at the end on this line:
return Response({'message': response['user']['created'], 'data': serializer.data},
status=status.HTTP_201_CREATED)
I am facing error like,
AttributeError: Got AttributeError when attempting to get a value for field `role` on serializer `RegistrationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'role'.
I think you need to use ModelSerializer
class RegistrationSerializer(serializers.Serializer):
to
class RegistrationSerializer(serializers.ModelSerializer):