Populate readonly nested serializer
Question:
I’m using ModelViewSets and I have nested serializer for these models.
class Profile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
place = models.OneToOneField(Place, on_delete=models.DO_NOTHING)
bio = models.TextField(null=True)
class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(max_length=150, blank=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
objects = CustomAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name']
def __str__(self):
return self.email
update
How can I autocomplete a newly created profile with 1:1 relation to User that is sending the request?
So, you are registering on a website, now you are a user but you want to have profile, some bio so you are setting up your profile for that
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ('password', 'is_superuser', 'is_staff', 'groups', 'user_permissions')
class ProfileSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
user = UserSerializer(read_only=True)
place = PlaceSerializer(read_only=True)
class Meta:
model = Profile
fields = '__all__'
depth = 1
viewsets
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
get_queryset = Profile.objects.all()
def create(self, request):
serializer = ProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
data = serializer.data
return Response(data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
As you can see I’m using ‘drf-writable-nested’ and it works okay but I can’t figure it out how to automatically connect profile to user on new profile creation.
Answers:
You will have CurrentUserDefault
if you want to use read_only field.
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
Or you can omit user and pass it while saving the serializer like this:
serializer.save(user=request.user)
To automatically connect a newly created profile to the user sending the request, you can modify the create
method in your ProfileViewSet
. Here’s one way to do it:
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
def create(self, request):
serializer = ProfileSerializer(data=request.data)
if serializer.is_valid():
# Set the user to the requesting user
serializer.save(user=request.user)
data = serializer.data
return Response(data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Note that this assumes that you are using token authentication or some other form of authentication that identifies the user making the request. If not, you will need to modify this code accordingly to identify the user in some other way.
I’m using ModelViewSets and I have nested serializer for these models.
class Profile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
place = models.OneToOneField(Place, on_delete=models.DO_NOTHING)
bio = models.TextField(null=True)
class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(max_length=150, blank=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
objects = CustomAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name']
def __str__(self):
return self.email
update
How can I autocomplete a newly created profile with 1:1 relation to User that is sending the request?
So, you are registering on a website, now you are a user but you want to have profile, some bio so you are setting up your profile for that
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ('password', 'is_superuser', 'is_staff', 'groups', 'user_permissions')
class ProfileSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
user = UserSerializer(read_only=True)
place = PlaceSerializer(read_only=True)
class Meta:
model = Profile
fields = '__all__'
depth = 1
viewsets
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
get_queryset = Profile.objects.all()
def create(self, request):
serializer = ProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
data = serializer.data
return Response(data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
As you can see I’m using ‘drf-writable-nested’ and it works okay but I can’t figure it out how to automatically connect profile to user on new profile creation.
You will have CurrentUserDefault
if you want to use read_only field.
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
Or you can omit user and pass it while saving the serializer like this:
serializer.save(user=request.user)
To automatically connect a newly created profile to the user sending the request, you can modify the create
method in your ProfileViewSet
. Here’s one way to do it:
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
def create(self, request):
serializer = ProfileSerializer(data=request.data)
if serializer.is_valid():
# Set the user to the requesting user
serializer.save(user=request.user)
data = serializer.data
return Response(data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Note that this assumes that you are using token authentication or some other form of authentication that identifies the user making the request. If not, you will need to modify this code accordingly to identify the user in some other way.