I have the following User,
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, max_length=255)
username = models.CharField(null=False, unique=True, max_length=255)
full_name = models.CharField(max_length=255, blank=True, null=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
And the following UserProfile model,
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, )
level = models.CharField(default="Noob", max_length=255)
reputation = models.IntegerField(default=0)
status = models.CharField(max_length=255, null=True, blank=True)
The User has a one to one relationship with Profile.
This is the UserSerializer,
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
location = LocationSerializer(read_only=True)
profile = UserProfileSerializer(read_only=True)
class Meta:
model = models.User
fields = (
'id', 'email', 'mobile', 'username', 'full_name', 'password', 'is_active', 'profile',
)
And this is the profile serializer.
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserProfile
fields = ('level', 'reputation', 'status',)
The issue is that in the serialized output for the user there is no nested profile data. How do I fix this. Any help appreciated.
all you need is set source for profile:
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
location = LocationSerializer(read_only=True)
profile = UserProfileSerializer(source='userprofile', read_only=True)
the userprofile is name of relation of your model User by onetoone to the UserProfle, other way you can set related_name for attribute user in the
UserProfle.
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)
then your serializer will work fine as is.
Related
I can't add a new user using Django Rest Framework. Here is my code from models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
password = models.CharField(max_length=15, default= None)
first_name = models.CharField(max_length=100, validators=[name_validator])
last_name = models.CharField(max_length=100, validators=[name_validator])
email = models.CharField(max_length=100, validators=[mail_validator])
created_at = models.DateTimeField(auto_now_add=True)
As you can see I am using models.OneToOneField cause I want to extend the default user to add some more fields.
Bellow is my serializers.py file:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
permissions_classes = [
permissions.AllowAny
]
fields = '__all__'
The viewset is the following:
class UserViewset(viewsets.ModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
When I go to my endpoints and try to add a new user, I cannot put anything in the "user" field:
Click for image
I am a beginner and it would be of great help.
Thank you!
you can to this
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
password = models.CharField(max_length=15, default= None)
first_name = models.CharField(max_length=100, validators=[name_validator])
last_name = models.CharField(max_length=100, validators=[name_validator])
email = models.CharField(max_length=100, validators=[mail_validator])
created_at = models.DateTimeField(auto_now_add=True)
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Profile
fields = '__all__'
views.py
class UserViewset(viewsets.ModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = [permission. AllowAny,]
When you want to extend the default user to add some more fields, the best way for that is substituting a custom User model by inheritance the AbstractUser model. Using this way, you can CRUD the user easily.
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 Django-Rest have a class User that contain the field first_name, and a class Account that contain the fields username and a_class_ref that is a one-to-one' relation.
How it is possible in the serializer of B to do something like :
class AccountSerializer():
class Meta:
model= Account
fields= [
'username',
'firstname`
]
Account :
class Account(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
related_name='account',
on_delete=models.CASCADE
)
def username(self):
return self.user.username <== this is the solution that I'm trying to avoid
And User is the extended AbstractUser from Django-rest-framework, that comes with a first_name = models.CharField(_('first name'), max_length=30, blank=True)
Thank you
You can declare a custom field with the source attribute:
class BSerializer(serializers.ModelSerializer):
a_field = serializers.CharField(source='a_class_ref.a_field')
class Meta:
model= B
fields= ['b_field', 'a_field']
Edit
Based on the models you posted, the following should work:
class Account(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
related_name='account',
on_delete=models.CASCADE
)
class AccountSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
firstname = serializers.CharField(source='user.first_name')
class Meta:
model= Account
fields= ['username', 'firstname']
I have a model named tranasaction.
class transactions(models.Model):
id = models.AutoField(primary_key=True)
date = models.DateTimeField(default=datetime.now, blank=True)
saleduser = models.ForeignKey('UserData',on_delete=models.CASCADE, related_name='vclouduser_data', blank=True, null=True)
brand = models.CharField(max_length=50,blank=True)
type = models.CharField(max_length=50,blank=True)
quantity = models.DecimalField(blank=True,null=True,max_digits=20,decimal_places=2)
amount = models.DecimalField(blank=True,null=True,max_digits=20,decimal_places=2)
And I need to create a summary report,
so I create a new Model.
class SaleSummary(transactions):
class Meta:
proxy = True
verbose_name = 'Sale Summary'
verbose_name_plural = 'Sales Summary'
what i need that i need to get the total amount of each type as a new model.
please help me to solve this.
Thanks in advance.
Here is an example of a proxy model for a normal model, with a filtering example. I hope it will be helpful for you that how can you create the proxy model and how can you filter specific data from any model for the admin site. You also can apply this for your custom API response, Then you should write this code in serializers. Best of luck.
models.py
class UserModel(AbstractBaseUser):
"""
Purpose: create a custom auth user table
"""
name = models.CharField('name of user',max_length=256)
email = models.EmailField('email address', unique=True)
date_joined = models.DateTimeField('user creation date', auto_now_add=True)
is_admin = models.BooleanField('is a admin', default=False)
is_staff = models.BooleanField('is a staff', default=False)
is_superuser = models.BooleanField('if a superuser', default=False)
status = models.ForeignKey(SesameUserStatus, on_delete = models.CASCADE, related_name='sesame_user_status_type', default=2)
organization = models.ForeignKey(Organization, on_delete = models.CASCADE, related_name='sesame_user_organization_info', default=1)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
def has_perm(self,prm,obj=None):
return self.is_active and self.is_admin
def has_module_perms(self,app_level):
return self.is_active and self.is_admin
def __str__(self):
return f'{self.name} // {self.email} // {self.organization.name}'
class Meta:
verbose_name = "User"
verbose_name_plural = "Users"
indexes = [
models.Index(fields=['email']),
]
class UserProxyModel(UserModel):
"""
Purpose: create proxy model to visualize only pending user
"""
class Meta:
proxy = True
verbose_name = "Pending User"
verbose_name_plural = "Pending Users"
###########################################################################
admin.py
class UserAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'email', 'date_joined', 'is_admin', 'is_staff']
class Meta:
model = UserModel
class UserProxyModelAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'email', 'date_joined', 'is_admin', 'is_staff', 'organization', 'status']
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs.filter(status=SESAME_USERS["status"]["PENDING"]).order_by('id')
return None
class Meta:
model = UserProxyModel
admin.site.register(UserModel, UserAdmin)
admin.site.register(UserProxyModel, UserProxyModelAdmin)