In my service, when registering a user, two entities must be created: the user and the company, to which the user entity will be immediately linked.
To do this, I created an endpoint in which I use two serializers (for the user and for the company). But there are some problems with this solution:
class UserCreateViewSet(mixins.CreateModelMixin,
viewsets.GenericViewSet):
"""
Creates user accounts
"""
queryset = User.objects.all()
serializer_class = CreateUserSerializer
permission_classes = (AllowAny,)
REQUIRED_FIELDS = (
'email', 'password',
'position', 'company_name',
'phone_number'
)
def create(self, request, *args, **kwargs):
data = request.data
provided_fields = set(data.keys())
for field in self.REQUIRED_FIELDS:
if field not in provided_fields:
return Response(
{"detail":f"{field} not provided"},
status=400
)
# --- Creating company for user
data['name'] = data['company_name']
company_serializer = CompanyReducedSerializer(data=request.data)
company_serializer.is_valid(raise_exception=True)
self.perform_create(company_serializer)
data['company'] = company_serializer.data['id']
# ---
# Code from super()
# --- Creating user
user_serializer = self.get_serializer(data=request.data)
user_serializer.is_valid(raise_exception=True)
self.perform_create(user_serializer)
headers = self.get_success_headers(user_serializer.data)
# ---
return Response(
status=201,
data=user_serializer.data,
headers=headers
)
I considered options using multi-table inheritance, but for some reasons, this solution does not suit me. The company and user entities must be separated.
I'd suggest a few changes for your code.
One of the ways can be as follows.
from rest_framework import serializers
class CreateUserSerializer(serializers.Serializer):
email = serializers.EmailField(required=True)
password = serializers.CharField(required=True)
position = serializers.CharField(required=True)
company_name = serializers.CharField(required=True)
phone_number = serializers.CharField(required=True)
from rest_framework import mixins, viewsets
from rest_framework.status import HTTP_201_CREATED
class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
...
def create(self, request, *args, **kwargs):
# this serializer will take care of your required fields
data_serializer = CreateUserSerializer(request.data)
data_serializer.is_valid(raise_exception=True)
company_serializer = CompanyCreateSerializer(data=request.data)
company_serializer.is_valid(raise_exception=True)
company = company_serializer.save()
# format data required for user creation
data = {"company": company, "email": email}
user_serializer = UserCreateSerializer(data=data)
user_serializer.is_valid(raise_exception=True)
user_serializer.save()
return Response(
data=user_serializer.data,
status=HTTP_201_CREATED
)
Other ways can be in the create
method of company or user serializer based on how you want to create the related objects
e.g.
class UserCreateSerializer(ModelSerializer):
...
def create(self, validated_data: dict):
company_sz = CompanySerializer(data=<YOUR_DATA>)
compant_sz.is_valid(raise_exception=True)
company_sz.save()
user = super().create(validated_data)
return user