Search code examples
djangodjango-rest-frameworkforeign-keys

DRF-How to save foreign keys data using APIView


everyone, I am absolutely a beginner in DjangoRestFramework. I have confusion to deal with relationships in DRF. How do I save foreign keys data using APIView?

models

   class User(AbstractUser):
        email = models.EmailField(max_length=255, unique=True)
        is_client = models.BooleanField(default=False)
        is_professional = models.BooleanField(default=False)
        
    
    class Client(models.Model):
            user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='client')
            ##
        
    class Professionals(models.Model):
            user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='professional')
            ##


    class HireProfessional(models.Model):
        client = models.ForeignKey(Client, related_name='user', on_delete=models.CASCADE)
        professional = models.ForeignKey(Professionals, on_delete=models.CASCADE, related_name="professsional")
        hire_start_date_time = models.DateTimeField(default=timezone.now)

Serializers

    class ProfessionalSerializer(serializers.ModelSerializer):
        profilePicture = serializers.ImageField(allow_empty_file=True, use_url='professional/profiles', required=False)
        skill = SkillSerializer(many=True,read_only=True)
        class Meta:
            model = Professionals
            fields = fields = ['first_name', 'last_name', 'profilePicture', 'profession', 'phone_number', 'experience', 'skill', 'charge_fee', 'about_me']


    class ClientSerializer(serializers.ModelSerializer):
        class Meta:
            model = Client
            fields = ['user_id', 'first_name', 'last_name', 'phone_number', 'profilePicture']

    class UserSerializer(serializers.ModelSerializer):
        client = ClientSerializer()
        professional = ProfessionalSerializer()
        class Meta:
        model = User
        fields = ('email', 'username', 'is_client', 'is_professional', 'client', 'professional')

    




    class HireProfessionalSerializer(serializers.ModelSerializer):
        client = ClientSerializer()
        professional = professionalSerializer()

        class Meta:
        model = HireProfessional
        fields = ['id','client', 'professional', 'hire_start_date_time']


views ##Edited

    
class HireProfessionalCreateApp(APIView):
    permission_classes = (IsAuthenticated, IsClientUser,)

    def current_user(self):
        user = self.request.user.client
        return user


    

    def post(self, request, username, format=None):
        try:
            professional = Professionals.objects.get(user__username=username)
            # print('hello', professional.user_id)
            data = request.data
            
            data['client'] = self.current_user()
            data['professional'] = professional.user_id

            serializer = HireProfessionalSerializer(data=data)
            data = {}
            if serializer.is_valid():
                hire = serializer.save()
                hire.save()
                                    
                return JsonResponse ({
                    "message":"professional hired success.",
                    # "remaning_time":remaning_datetime,
                    "success" : True,
                    "result" : serializer.data,
                    "status" : status.HTTP_201_CREATED
                })
            else:
                data = serializer.errors
                print(data)


            return Response(serializer.data)
         except Professionals.DoesNotExist:
            return JsonResponse ({"status":status.HTTP_404_NOT_FOUND, 'message':'professional does not exists'})

This is a hiring app.

Client able to hire to professional

client=logged in user

professional=passed id or username through URL

like: path('hire-professional/<professional id or username>', views)

Does anybody have any idea? how to solve it.


Solution

  • Consider using a ModelViewSet instead of an APIView since you're directly modifying the HireProfessional model. Additionally, the model uses ForeignKey fields for Client and Professional, you do not need to include their respective serializers in HireProfessionalSerializer.

    Implementing just the ModelViewSet (and adding it to the urls.py using a router) will mean that a user can select the client & professional themself, which means we're not done yet. It is recommended to use a router in DRF instead of adding all the views to urls.py manually.

    You can use the ModelViewSet to override the perform_create and perform_update functions in which you autofill serializer fields. :

    default create function

    def perform_create(self, serializer):
           serializer.save()
    

    autofill example

    def perform_create(self, serializer):
            # client is derived from logged in user
            client = Client.objects.get(user=self.request.user)
    
            # Autofill FK Client.
            serializer.save(client=client)
    

    Now the client is autofilled, but the professional isn't yet. If you want to also autofill the professional you will have to look into using a nested router since you want to retrieve the PK(id) from the URL. If you do so your URL would look something like this:

    url='/professionals/{professionals_pk}/hire/'
    

    I hope this gives you an idea of where to start. If you have any questions do let me know.