Django signals doesnt save models with custom params - python

I have a custom user model CustomUser and related models Employee with Foreignkey field to user.
class Employee(models.Model):
"""Employee information."""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=True)
first_name = models.CharField("first name", max_length=32, blank=True)
last_name = models.CharField("last name", max_length=32, blank=True)
I try to create Employee instance after user registration using post_save signals with fields first_name and last_name imported from my user model fields.
#receiver(post_save, sender=CustomUser)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
if instance.first_name and instance.last_name:
employee = Employee.objects.create(user=instance, first_name=instance.cleaned_data['first_name'], last_name=instance.clened_data['last_name'] )
else:
employee = Employee.objects.create(user=instance)
But always created model with blank field. What is the reason for this and what needs to be changed?
p.s. I need the same filds at both models for some reason.

Related

Queries related to Django Signals

To begin with, Here Profile and Seller model is created when a User model is created through Signals.What I want to do is When profile model is first created or updated,I want all the fields of Seller model to be same as all fields of Profile.Similarly when I first created Seller model,I also want all fields of Profile Model to be same as that of Seller model.But,I couldn't figure out how to do?
from typing import Tuple
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.db.models.fields import DecimalField
from django.dispatch import receiver
from django.db.models.signals import post_save
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True) #cascade is for deleting the customer
first_name = models.CharField(max_length=10, null=True)
second_name=models.CharField(max_length=10,null=True)
email = models.EmailField(max_length=70, null=True,blank=True)
#receiver(post_save, sender=User)
def create_profile(sender, instance,created,**kwargs):#Signal receivers must accept keyword arguments (**kwargs).
if created:
Profile.objects.create(user=instance)
...
#receiver(post_save, sender=Profile)
def create_seller(sender, instance,created,**kwargs):#Signal receivers must accept keyword arguments (**kwargs).
if created:
Seller.objects.create(user=instance)
class Seller(models.Model):
user = models.OneToOneField(Profile, on_delete=models.CASCADE, null=True, blank=True) #cascade is for deleting the customer
first_name = models.CharField(max_length=10, null=True)
second_name=models.CharField(max_length=10,null=True)
email = models.EmailField(max_length=70, null=True,blank=True)
...
If created=True it means a Profile object is created. otherwise if created=False it means that a Profile object has been updated.
When created=True you need to create a Seller object and when created=False you need to update a Seller object.
You can do this:
#receiver(post_save, sender=Profile)
def update_or_create_seller(sender, instance, created, **kwargs):
if created:
Seller.objects.create(
user=instance.user,
first_name=instance.first_name,
second_name=instance.second_name,
email=instance.email
)
else:
seller = instance.user.seller
seller.first_name = instance.first_name
seller.last_name = instance.second_name
seller.email = instance.email
seller.save()
Also notice that unless you define related_name in your OneToOneField, Django will use lowercased model name to access related object. So, instance.user.seller should work.

What is this User parameter in Django?

What is the User model we are importing here? What does it do
here?
from django.contrib.auth.models import User
class Customer(models.Model):
user=models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
name=models.CharField(max_length=200, null=True)
phone=models.CharField(max_length=200, null=True)
email=models.CharField(max_length=200, null=True)
profile_pic=models.ImageField(default="profile2.png",null=True,blank=True)
def __str__(self):
return self.name
user=models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
Above line is making a one-to-one relationship with the default Django User model with the customer model. This means, every User instance in your database can be associated with atmost 1 Customer instance.
The default User model contains standard user fields like username, email, first_name, last_name etc. Take a look here to learn more about the User model.
Read this to learn more about one-to-one relationships.

Many to Many or One to Many Django

I have the following two models in Django. One is basically an extension of the base Django user class and the other is a company model. I want to say that a user can belong to one or more companies and that a company can also have one or more contacts = "Users". Would this be a correct setup? How should I represent the tie between user and company?
User Profile model:
class Profile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Company model:
class Company(models.Model):
name = models.CharField(max_length=120)
account_name = models.CharField(max_length=10, default="")
sales_rep = models.ForeignKey(User, related_name="%(app_label)s_%(class)s_sales", default="")
csr = models.ForeignKey(User, related_name="%(app_label)s_%(class)s_csr", default="")
class CompanyContact(models.Model):
name = models.CharField(max_length=40, default="")
email = models.CharField(max_length=50, default="")
user = models.ForeignKey(User)
company = models.ForeignKey(Company)
First, is there a reason to extend the User model? The default model already includes a first_name and last_name field, so you don't need an additional model just for that data. Similarly, you don't really need CompanyContact because the User model also contains email and name (again, through first_name and last_name) fields.
You can add in your contacts as a ManyToManyField. If you want to use the custom Profile model instead of User, just replace User (in the ManyToManyField) with Profile.
class Company(models.Model):
name = models.CharField(max_length=120)
account_name = models.CharField(max_length=10, default="")
sales_rep = models.ForeignKey(User, related_name="%(app_label)s_%(class)s_sales", default="")
csr = models.ForeignKey(User, related_name="%(app_label)s_%(class)s_csr", default="")
contacts = models.ManyToManyField(User) # or Profile
This allows each company to have many contacts and each user to be a contact of many companies – thus many-to-many.
Now, if you wanted extra data to describe the many-to-many relationship, you can have another model for that. For example, you may want to keep a record if the contact is still active or what their role is. So, you may have a CompanyContact model that is similar to:
class CompanyContact(models.Model):
active = models.BooleanField(default=False)
role = models.CharField(max_length=50, default="")
user = models.ForeignKey(User) # or Profile
company = models.ForeignKey(Company)
Then, declare the ManyToManyField relationship to use this new model:
class Company(models.Model):
...
contacts = models.ManyToManyField(User, through="CompanyContact")
# or contacts = models.ManyToManyField(Profile, through="CompanyContact")

In Django Extending User Form for Custom User

I extended my User Model as described in this SO Posting:
Extending the User model with custom fields in Django
However, I'm trying to create a User Create form but I get the following:
'Members' object has no attribute 'set_password'
Here is my model form:
class Members(models.Model):
user = models.OneToOneField(User)
GENDER_CHOICES = ( ... )
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
date_of_birth = models.DateField()
class Meta:
db_table='members'
def create_user_profile(sender, instance, created, **kwargs):
if created:
Members.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
....and my form....
class SignUpForm(UserCreationForm):
GENDER_CHOICES = ( ... )
email = forms.EmailField(label='Email address', max_length=75)
first_name = forms.CharField(label='First Name')
last_name = forms.CharField(label='Last Name')
gender = forms.ChoiceField(widget=RadioSelect, choices=GENDER_CHOICES)
date_of_birth = forms.DateField(initial=datetime.date.today)
class Meta:
model = Members
fields = ('username', 'email','first_name', 'last_name')
I'm new at Django,so thanks in advance
The method you chose to extend your User model is by creating a UserProfile (which you've called Member). A Member is not a subclass of User, so you can't call User methods (like set_password) on it.
Instead, your SignUpForm's Meta model should still be User, and to get the extended UserProfile, you should call user.get_profile(). For instance, to get a user's gender, you would call user.get_profile().gender.
Read https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users for more information about extending the user profile.

Django: Lookup User OnetoOne Field using username with Model View Sets (Django Rest API)

My task is for an administrator in my application to be able to create and update an employee's details. Given that django's user model simplifies authentication, I used it as a OnetoOneField in my Employee Model, representing the key as the employee ID (username).
My Model -
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50, unique=True)
date_of_join = models.DateField(blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
designation = models.CharField(max_length=255, null=True)
mobile = models.CharField(max_length=255, null=True)
personal_email = models.CharField(max_length=255, blank=True, null=True)
official_email = models.CharField(max_length=255, blank=True, null=True)
current_station = models.CharField(
max_length=255, default="Chennai", null=True)
def __str__(self):
return self.name
Serializers -
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ('user', 'name', 'date_of_join', 'date_of_birth',
'designation', 'mobile', 'landline', 'personal_email',
'official_email', 'current_station')
My Model View Set:
class EmployeeListSet(viewsets.ModelViewSet):
lookup_field = 'user'
serializer_class = EmployeeSerializer
queryset = Employee.objects.all()
Browsable API of a specific Employee filtered by user ID
As shown in the image, the user field shows me pk instead of user.username.
I am able to see the username in the HTML Form for POST in the browsable API, however the json does not return the username by default and rather returns pk.
I want to be able to lookup and update an employee's details based on the username (employee ID).
What I have tried -
I have tried redefining the user field as a SerializedMethodField that returns user.username, but lookup and POST method still requires the pk instead of username, so it doesn't solve the problem for me.
Nesting serialziers makes the nested serializer have to be read only, which again makes my task of updating employee details undesirable.
How can I lookup an employee object based on user.username instead of pk?
How can I associate an existing User object with an employee object during creation with the User object's username using modelviewsets? Is there a way to solve this without having to override or write my own create and update functions for the modelviewset?

Categories

Resources