I am currently trying to implement the following serializer to the Profile serializer. But I would like to add a condition to it.
Profile serializer
class UserProfileSerializer(serializers.ModelSerializer):
role = serializers.ChoiceField(choices=(('Reader', u'Reader'), ('Author', u'Author'), ('Admin', u'Admin')))
role_display = serializers.SerializerMethodField()
class Meta:
model = Profile
fields = ('gender', 'birthday', 'icon', 'role', 'role_display')
depth = 1
Author serializer
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = '__all__'
Reader serializer
class ReaderSerializer(serializers.ModelSerializer):
class Meta:
model = Reader
fields = '__all__'
Both author and reader table has a one-to-one relationship towards profile table.
Depending on the role option I would like to show a specific nested serializer.
Example:
{
"id": 19,
"username": "maoji1",
"password": "pbkdf2_sha256$180000$YhzDiqzJ4OyC$syzkwR5X3/H2p5NTB0JEK2zS5nvYu5ddHrTgy3cYU/E=",
"email": "pbkdf2_sha256$180000$YhzDiqzJ4OyC$syzkwR5X3/H2p5NTB0JEK2zS5nvYu5ddHrTgy3cYU/E=",
"profile": {
"gender": "male",
"birthday": "2020-03-10",
"icon": null,
"role": {
is_vip:True,
validate_date:...
}
}
}
Reader model
class Reader(models.Model):
user = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name='reader', verbose_name='user')
is_user_vip = models.CharField(choices=(('normal', u'Normal'), ('vip', u'VIP')),
default='normal',
max_length=10,
verbose_name=u'Vip status')
vip_validate = models.DateField(verbose_name=u"Validate Until",
auto_now_add=True, null=True,
editable=False)
Author model
class Author(models.Model):
user = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name='author')
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='Book', null=True)
contract_number = models.IntegerField(verbose_name='Contact number', null=True, blank=True)
Profile model
class Profile(models.Model):
role_choice = (
('Reader', u'Reader'),
('Author', u'Author'),
('Editor', u'Editor'),
('Admin', u'Admin')
)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile', verbose_name='user')
gender = models.CharField(choices=(("male", u"Male"), ("female", u"Female")),
default="Female",
max_length=150, verbose_name='Gender')
birthday = models.DateField(null=True, blank=True, verbose_name="Birthday")
icon = models.ImageField(upload_to="media/image/%Y/%m",
default=u"media/image/default.png",
max_length=1000,
verbose_name=u"User icon", null=True)
role = models.CharField(choices=role_choice,
max_length=150,
default='Admin',
verbose_name='Role')
You can use SerializerMethodField and decide which serializer you must use inside it's method:
class UserSerializer(serializers.ModelSerializer):
profile = serializers.MethodField()
class Meta:
model = Profile
fields = ('id', 'username', 'password', 'email','profile')
def get_profile(self,obj):
return ProfileSerializer(obj.profile).data
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields ='__all__'
class ReaderSerializer(serializers.ModelSerializer):
class Meta:
model = Reader
fields ='__all__'
class ProfileSerializer(serializers.ModelSerializer):
role = serializers.MethodField()
class Meta:
model = Profile
fields = ('gender', 'birthday', 'icon', 'role')
def get_role(self,obj):
if hasattr(obj, 'author')
serializer = AuthorSerializer(obj.author)
return serializer.data
elif hasattr(self,'reader'):
serializer = ReaderSerializer(obj.reader)
return serializer.data
return {} # if your profile doesn't have any relation with author or reader
class UserSerializer(serializers.ModelSerializer):
profile = serializers.MethodField()
class Meta:
model = User
fields = ('id', 'username', 'password', 'email','profile')
def get_profile(self,obj):
return ProfileSerializer(obj.profile).data
Related
Hello guys I am new in Django REST API. I want your help.
I am trying to join my two models : User and blogs to get specific api output like this:
{
"blog_id": 1,
"title": "first blog",
"description": "hola",
"image": "/images/phone.jpg",
"create_at": "2021-04-08T14:24:51.122272Z",
"update_at": "2021-04-08T14:37:00.287746Z",
"user": 1,
"user_name": "superuser",
"first_name": "Dannis",
"email": "superuser#test.com"
}
Here is the models.py
class Blog(models.Model):
blog_id = models.AutoField(primary_key=True, editable=False)
title = models.CharField(max_length=128,null=False,blank=False)
description = models.TextField(null=True,blank=True)
image=models.ImageField(null=True,blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
create_at = models.DateTimeField(auto_now_add=True, editable=False)
update_at = models.DateTimeField(auto_now=True,editable=False)
def __str__(self):
return f'{self.user.username} {self.title} {self.create_at} {self.update_at}'
class UserActive(models.Model):
user_active_id = models.AutoField(primary_key=True, editable=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
last_active = models.DateTimeField(auto_now_add=True, editable=False)
Here is the views.py
#api_view(['GET'])
def get_blogs(request):
blogs = Blog.objects.all()
serializer = BlogSerializers(blogs, many=True)
return Response(serializer.data)
#api_view(['GET'])
def get_users(request):
user = User.objects.all()
serializer = UserSerializer(user, many=True)
return Response(serializer.data)
here is the serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'is_staff', ]
class BlogSerializers(serializers.ModelSerializer):
class Meta:
model = Blog
fields = '__all__'
Please Help me out. I will be so grateful
You need to tell the BlogSerializer to use the UserSerializer for the user field:
class BlogSerializers(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Blog
fields = '__all__'
I have a few models to represent a user. A user has a garden, a profile and a gardener_profile. When serialising the user objects, garden and profile are getting showed, but gardener_profile is not. All of them are one to one relations. In my swagger doc the gardener_profile is recognized, but not in the response object.
Here are the serializers:
class WorkingHoursSerializer(serializers.ModelSerializer):
gardener_profile = serializers.StringRelatedField(read_only=True)
class Meta:
model = WorkingHours
fields = '__all__'
class GardenSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=False, queryset=GardreamUser.objects.all())
class Meta:
model = Garden
fields = ['id', 'url', 'grass', 'beds', 'terrace', 'tracks', 'entry', 'user']
class GardenerProfileSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=False, queryset=GardreamUser.objects.all())
working_hours = WorkingHoursSerializer(many=True)
class Meta:
model = GardenerProfile
fields = ['id', 'url', 'working_radius', 'salary', 'iban', 'contract', 'user', 'working_hours']
def create(self, validated_data):
working_hours_data = validated_data.pop('working_hours')
gardener_profile = GardenerProfile.objects.create(**validated_data)
for working_hour_data in working_hours_data:
WorkingHours.objects.create(gardener_profile=gardener_profile, **working_hour_data)
return gardener_profile
class UserProfileSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = UserProfile
fields = '__all__'
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = UserProfileSerializer(required=True)
garden = GardenSerializer(read_only=True)
gardener_profile = GardenerProfileSerializer(read_only=True)
class Meta:
model = CustomUser
fields = ['id', 'url', 'username', 'email', 'first_name', 'last_name', 'password', 'groups', 'profile',
'garden', 'gardener_profile']
extra_kwargs = {'password': {'write_only': True}}
And here are the models:
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
address = models.CharField(max_length=255)
country = models.CharField(max_length=50)
city = models.CharField(max_length=50)
zip = models.CharField(max_length=5)
class Garden(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
grass = models.DecimalField(max_digits=6, decimal_places=2)
terrace = models.DecimalField(max_digits=6, decimal_places=2)
class GardenerProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
salary = models.DecimalField(max_digits=6, decimal_places=2)
contract = models.FileField(null=True, blank=True)
class WorkingHours(models.Model):
gardener_profile = models.ForeignKey(GardenerProfile, related_name='working_hours', on_delete=models.CASCADE)
weekday = models.IntegerField(choices=WEEKDAYS)
from_hour = models.TimeField()
to_hour = models.TimeField()
class Meta:
ordering = ('weekday', 'from_hour')
unique_together = ('weekday', 'gardener_profile')
I found the solution: add related_name='gardener_profile' to the user field at GardenerProfile
I'm trying to create create a nested serializer using the Django Rest framework. The relationship is Profile X User but when i use Profile.objects.create(user=profile, **user_data) i get ValueError: Cannot assign "<Profile: Profile object (7)>": "Profile.user" must be a "User" instance..
This should be some rookie misunderstanding of models relationship definitions or the serializer declaration itself but I can't find anything around the docs. If someone can point me a direction I'll be gracefull.
models.py
class User(models.Model):
name = models.CharField(max_length=100, blank=False)
email = models.CharField(max_length=100, blank=True, default='')
password = models.CharField(max_length=100, blank=True, default='')
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
class Profile(models.Model):
# choices [...]
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
profile_type = models.CharField(max_length=2,choices=PROFILE_CHOICES,default=TEAMMEMBER)
authentication_token = models.CharField(max_length=100, null=True)
avatar_url = models.CharField(max_length=100, default='')
permissions = models.CharField(max_length=100, null=True)
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
serializer.py
class UserSerlializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['name', 'email', 'password']
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
Profile.objects.create(user=profile, **user_data)
return Profile
POST
{
"profile_type" : "ST",
"user": {
"name" : "test",
"email" : "test#test.com",
"password" : "123456"
}
}
You are creating instances in wrong way. Change your create(...) method as,
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
user_instance = User.objects.create(**user_data)
profile_instance = Profile.objects.create(user=user_instance, **validated_data)
return profile_instance
Profile.user should beUser instance, but you are assigning Profile instance.
Change your create method to this:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
user = User.objects.create(**user_data) # 1. creating user
profile.user = user # 2. assigning user
profile.save() # 3. saving profile after adding user
return profile # returning Profile instance.
inherit your user model from django contrib auth module also, and make a one to one relation with profile
from django.contrib.auth.models import User
I was trying to add new relation to many to many records,
for example i have these models:
models.py
class Team(models.Model):
name = models.CharField(blank=True, unique=True, max_length=100)
players = models.ManyToManyField(User, blank=True, related_name='players')
class TeamInvite(models.Model):
from_user = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='invite_by', blank=True, null=True)
to_user = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='invite_to', blank=True, null=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='invite_to_team', blank=True, null=True)
status = models.NullBooleanField(blank=True, null=True, default=None,)
and my serializer:
serializers.py
class TeamInviteCreateSerializer(serializers.ModelSerializer):
team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all())
from_user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
class Meta:
model = TeamInvite
fields = ('id', 'from_user', 'to_user', 'team', 'status')
after that the user which in to_user will take an action to TeamInvite like accept or decline.
I need the serializer which will take the new user and add him to the existing team like the following serializer:
class TeamInviteAcceptDeclineSerializer(serializers.ModelSerializer):
method_name = serializers.SerializerMethodField()
class Meta:
model = TeamInvite
fields = ('id', 'from_user', 'date_time', 'team', 'method_name', 'status')
def get_method_name(self, *args, **kwargs):
method_name = None # kwargs['context']['request'].method_name
return method_name
def update(self, instance, validated_data):
instance.team = validated_data.get('team', instance.team)
method_name = validated_data.get('method_name')
instance.status = validated_data.get('status', instance.status)
instance.to_user = validated_data.get('to_user', instance.to_user)
if method_name == 'decline':
instance.status = False
else:
instance.status = True
team = Team.objects.get(pk=instance.team.pk)
team.players.add(instance.to_user)
# team.players.create(team_id=team, user_id=instance.to_user)
team.save()
instance.save()
return instance
update function does not add the user to existing team and doesn't raise any error either. What am i missing here?
my request was:
{
"from_user": 1,
"to_user": 23
"team": 64,
"method_name": "accept",
"status": null
}
thank you
I got the missing point in my code..
it was in:
class TeamInviteAcceptDeclineSerializer(serializers.ModelSerializer):
method_name = serializers.SerializerMethodField()
class Meta:
model = TeamInvite
fields = ('id', 'from_user', 'date_time', 'team', 'method_name', 'status')
fields = missed 'to_user' pram
I have two models CustomUser and UserProfile.
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), max_length=254, unique=True, blank=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
owner_of = models.CharField(max_length=100, blank=True)
total_years= models.CharField(_('In Current Profession Since'),max_length=100,blank=True, null=True)
serializers.py:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('id', 'email','first_name', 'last_name',)
class ProfileSerializer(serializers.ModelSerializer):
user = AccountSerializer(read_only=True,required=False)
class Meta:
model = UserProfile
fields = ('user','owner_of','total_years','first_name','last_name','email')
Error I get
"Field name first_name is not valid for model UserProfile."
How to combine two model fields and make create and update function
Your profileserializer should be like this,
class ProfileSerializer(serializers.ModelSerializer):
user = AccountSerializer(required=True)
class Meta:
model = UserProfile
fields=('user','owner_of','total_years')
which results like this,
{
{
"user":
{
"id: ...,
"first_name": ...,
"last_name": ...,
"email": ...
},
"owner_of": ...,
"total_years" ...
},
...
}
More detailed explanation is here.
By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create create() and/or update() methods in order to explicitly specify how the child relationships should be saved.
class ProfileSerializer(serializers.ModelSerializer):
user = AccountSerializer(required=True)
class Meta:
model = UserProfile
fields=('user','owner_of','total_years')
def create(self, validated_data):
user_data = validated_data.pop('user')
user= CustomUser.objects.create(**user_data)
user_profile=UserProfile.objects.create(user=user, **validated_data)
return user_profile
def update(self, instance, validated_data):
user_data = validated_data.pop('user', None)
user = instance.user
instance.owner_of = validated_data.get("owner_of", instance.owner_of)
instance.total_years= validated_data.get("total_years", instance.total_years)
instance.save()
if user_data:
user.first_name = user_data.get("first_name", user.first_name)
user.last_name = user_data.get("last_name", user.last_name)
user.email= user_data.get("email", user.email)
user.save()