I have a custom User model and the User Profile model.
class User(AbstractUser):
"""Custom User authentication class to use email as username"""
username = None
email = models.EmailField(verbose_name='email', max_length=255, unique=True,
error_messages={
'unique': _(
"A user is already registered with this email address"),
}, )
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
class UserProfile(models.Model):
user = models.OneToOneField(User, to_field='email', on_delete=models.CASCADE)
emp_id = models.CharField(max_length=20, blank=False, default='0', null=False)
department = models.CharField(max_length=50, blank=True, default='', null=True)
I am trying to write a serializer that combines both these models are produces a nested JSON.
for example:
{
"email":"user#gmail.com",
"is_active":true,
"profile":
{
"emp_id":2,
"department":2
}
}
This is what I tried to do
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('id', 'user', 'emp_id', 'department')
class UserPairSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer(read_only=True)
class Meta:
model = User
fields = ('id', 'email', 'is_active', 'profile')
But for some reason, there is neither the field profile in my response nor am I getting any errors.
I tried following this docs: https://www.django-rest-framework.org/api-guide/relations/
What is the issue and how do I solve this?
As per the documentation implicitly refering to this, 'reverse' queries are done using the name of the Model, lowercased (in this case user.userprofile).
So you have two options:
Either you specify a custom related_name on the user field on the UserProfile model.
class UserProfile(models.Model):
user = models.OneToOneField(User, to_field='email', on_delete=models.CASCADE, related_name='profile')
Or, you specify a source argument on your nested serializer (see documentation):
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('id', 'user', 'emp_id', 'department')
class UserPairSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer(read_only=True, source='userprofile')
class Meta:
model = User
fields = ('id', 'email', 'is_active', 'profile')
Related
Can anybody explain me why am in getting this error.
I have two models User and Profile with OnetoOne relations.
models.py
class User(AbstractBaseUser):
phone_number = models.IntegerField(unique=True, verbose_name='phone number')
email = models.EmailField()
class Profile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
and here's the serializer.py
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields =["first_name", "last_name"]
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ["id", "phone_number", "email", "profile",]
Views.py
class UserDetailsView(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
def get_queryset(self):
return User.objects.filter(pk=self.kwargs['pk'])
You write the serializer in wrong way. The profile model has user column & user table doesn't have profile column and one thing Use OneToOne filed when creating a profile model against a user. Because one profile can contain one user.
models.py
class User(AbstractBaseUser):
phone_number = models.IntegerField(unique=True, verbose_name='phone
number')
email = models.EmailField()
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,)
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "phone_number", "email"]
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields =["first_name", "last_name", "user"]
for more information about nested serializer please follow the official documentation
I am creating rest APIs for a website in which users can purchase one of the provided subscriptions.
In this website there is a user-info API which returns the information about the logged in user which can be used to show their info on the website.
The problem is that, the mentioned API's serializer is a modelSerializer on the "User" model and the information that I want to return is the instance of "Subscription" model which the latest instance of "SubPurchase" model refers to.
These are my serializers, models and views.And I need to somehow return the user's current subscription's ID and name along with the user's information. If you have any further questions, ask me in the comments and I'll answer them.
# models.py
class User(AbstractBaseUser, PermissionsMixin):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
isSuspended = models.BooleanField(default=False)
isAdmin = models.BooleanField(default=False)
emailActivation = models.BooleanField(default=False)
balance = models.IntegerField(default=0)
objects = UserManager()
USERNAME_FIELD = 'username'
class Subscription(models.Model):
subID = models.AutoField(primary_key=True)
nameOf = models.CharField(max_length=50)
price = models.PositiveIntegerField()
salePercentage = models.PositiveIntegerField(default=0)
saleExpiration = models.DateTimeField(default=datetime.datetime.now, blank=True)
def __str__(self):
return f"{self.nameOf}"
class SubPurchase(models.Model):
price = models.PositiveIntegerField()
dateOf = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
subscription = models.ForeignKey(Subscription, null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.subscription
# serializers.py
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
# views.py
class UserInfoViewSet(viewsets.ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserInfoSerializer
def get_queryset(self):
uID = getattr(self.request.user,'userID')
return get_user_model().objects.filter(userID=uID)
def get_object(self):
uID = getattr(self.request.user,'userID')
return self.queryset.filter(userID=uID)
Again, I need to change the UserInfoSerializer in a way that would give me the user's current subscription's name, ID and expiration date which would be 30 days after the purchase date
If you are only interested in the returned data, you can override the function to_representation of your serializer and create a serializer for your related model. If I understood correctly, the current subscription of your user is the last one (if sorted by "dateOf"). So something like that could do the trick
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = ('nameOf', 'id', 'saleExpiration ')
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
def to_representation(self, instance):
data = super().to_representation(instance)
current_subs = instance.subpurchase_set.order_by('dateOf').last().subscription
data['current_subscription'] = SubscriptionSerializer(instance=current_subs).data
return data
you can use NestedSerializers to achieve what you are looking for
basically, nested serialization is a method in which you can return, create, put..., into a model from another model, it goes like this..
models.py
class User(AbstractBaseUser, PermissionsMixin):
....
#user model data
class SubPurchase(models.Model):
...
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
serializers.py
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields =["anyfield you wanna include"]
class SubPurchaseSerializer(serializers.ModelSerializer):
class Meta:
model = SubPurchase
fields =["anyfield you wanna include"]
class UserInfoSerializer(serializers.ModelSerializer):
subpurchace = SubPurchaseSerializer()
subscription= SubscriptionSerializer() #later included in the fields of this serializer
class Meta:
model = get_user_model()
fields = ('userID','subpurchace', 'subscription', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
How to save the object with one-to-one relation and having parent_link=True using serializer. Below are my models and serializer having some fields from the actual model that I wanted to implement. I am not able to save the 'user' relation in the database. It is throwing the integrity error.
class Audit(models.Model):
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True, null=True)
updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta:
abstract = True
class User(Audit):
class Meta:
db_table = 'user'
email = models.EmailField(unique=True)
password = models.TextField()
is_active = models.BooleanField(default=False)
class UserProfile(User):
class Meta:
db_table = 'user_profile'
user = models.OneToOneField(User, on_delete=models.CASCADE, parent_link=True,
primary_key=True)
address = models.TextField(null=True)
dob = models.DateField()
language = models.CharField(max_length=50, null=True)
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['user', 'address', 'dob', 'language']
And the requested data looks like this.
{
"email": "abc#pqr.com",
"password": "1234",
"dob": "2021-12-11",
"language" : "English"
}
You can pass user when saving serializer :
serializer.save(user=<USER>)
Note that if you want to create user when creating user profile, you should override create() method in your serializer, first create your user, then create user profile, and pass created user to it
I am facing one issue for updating models using django serializer.
Here is my models:
class User(models.Model):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class UserProfile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
designation = models.CharField(max_length=200, blank=True)
contact_number = models.CharField(max_length=20, blank=True)
team = models.CharField(max_length=200, blank=True)
manager = models.CharField(max_length=200, blank=True)
joining_date = models.DateField(default=datetime.now)
I need to create a serializer for editing profile details of the current user. In this User details like designation, contact_number , team , manager, joining_date are in UserProfile model and te first_name and last_name are in User model .... At one edit both the models needs to get update
This is my serializer.
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name','last_name')
class UserProfileSerializer(ModelSerializer):
user = UserSerializer()
class Meta:
model = UserProfile
fields = ('id', 'designation', 'contact_number', 'team', 'manager',
'joining_date','user')
def update(self, instance, validated_data):
user = validated_data.get('user')
instance.user.first_name = user.get('first_name')
instance.user.save()
return instance
I am getting an error {
"user": [
"This field is required."
]
}
Change user field to DictField. In this way, it will not be treated as foreignkey.
In to_representation, serialize user object and you will get data in format in which you want to get.
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name','last_name')
class UserProfileSerializer(ModelSerializer):
user = serializer.DictField(required=False, write_only=True, default={})
def to_representation(self, instance):
data = super().to_representation(instance)
data.update({'user': UserSerializer(instance.user).data})
return data
class Meta:
model = UserProfile
fields = ('id', 'designation', 'contact_number', 'team', 'manager',
'joining_date','user')
def update(self, instance, validated_data):
user = validated_data.get('user')
instance.user.first_name = user.get('first_name')
instance.user.save()
return instance
I would like to modify Django UserCreationForm so that it would support creating my custom user. There are required fields company and role which both should offer some kind of selection to pick the correct choice (there will be only 3 roles but there can be hundreds of companies).
I believe I need to extend UserCreationForm and modify UserAdmin. However, I have followed several different examples but so far in vain. Below is the model. How can I command Django to add the extra fields to the user creation form?
ROLE_CHOICES = [
('role1', 'Role 1'),
('role1', 'Role 2'),
('janitor', 'Janitor'),
]
class Company(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
class Meta:
ordering = ('created',)
db_table = "company"
def __str__(self):
return self.name
class CustomUser(AbstractUser):
created = models.DateTimeField(auto_now_add=True)
username = models.CharField(max_length=100, unique=True)
email = models.EmailField(max_length=200, unique=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='%(class)s_company')
role = models.CharField(
max_length=100,
choices=ROLE_CHOICES,
default='janitor',
)
phone_number = models.CharField(null=True, blank=True, max_length=20)
class Meta:
ordering = ('created',)
db_table = "custom_user"
def __str__(self):
return self.username
You don't have to extend UserCreationForm. just use this:
forms.py
from django import forms
from .models import CustomUser
class UserRegistrationForm(forms.ModelForm): # you can name it anything
class Meta:
model = CustomUser
fields = ('username', 'email', 'company',....) # add the fields here you want in form just not created. it's auto fill
Use this form.
If you want admin. Write this in admins.py
from .models import CustomUser
class CustomUserAdmin(admin.ModelAdmin):
list_display = ('username', 'email', 'company'...) # add fields as you want
admin.site.register(CustomUser, CustomUserAdmin)
Hope this help. If not, please comment.