TIL

Web Dev Bootcamp TIL Day-50(DRF Serializers / Query Parameters)

frannyk 2022. 6. 24. 22:18
  • Query parameters are a defined set of parameters attached to the end of a url.
    • They are extensions of the URL that are used to help define specific content or actions based on the data being passed.
    • To append query params to the end of a URL, a ‘?’ Is added followed immediately by a query parameter.

Sending Query Parameters via Postman

  • Let's say we want to search for jobs posts that require certain skills using query parameters:
    • First, let's make sure that skillsets and job posts are in a many-to-many relationship
    • Notice that we can use an intermediate table to manage the relationship betwen the two models:
class SkillSet(models.Model):
    name = models.CharField(max_length=128)
    job_posts = models.ManyToManyField('JobPost', through='JobPostSkillSet')

    class Meta:
        db_table = 'skill_sets'

class JobPostSkillSet(models.Model):
    skill_set = models.ForeignKey('SkillSet', on_delete=models.SET_NULL, null=True)
    job_post = models.ForeignKey('JobPost', on_delete=models.SET_NULL, null=True)

class JobPost(models.Model):
    job_type = models.ForeignKey(JobType, on_delete=models.SET_NULL, null=True)
    company = models.ForeignKey('Company', on_delete=models.SET_NULL, null=True)
    job_description = models.TextField()
    salary = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'job_posts'

skill_sets table
jobpostskillset table(intermediate table)

  • Query Parameters and Reverse Lookups
    • First we want to return the queryset of JobPostSkillSet objects that fit our search criteria
    • From that queryset, we then return the queryset of corresponding JobPost objects
      • In this particular example, let's say we send skills = ['node', 'postgresql'] as query parameters
      • These skillsets have id values of 4 and 5 respectively.
      • Notice that our JobPostSkillSet queryset returns instances 1, 5 and 6 even though instance 5 does not contain our skillset parameter.
      • This is due to the fact that JobPost instance 3 requires skills = ['mysql', 'node'] such that the JobPostSkillSet queryset will return the JobPostSkillSet instance that takes JobPost instance 3 and SkillSet instance 3 as Foreign Keys.
class JobPostView(APIView):
	def get(self, request):
        # we can receive all query parameter values w/ the same key values
        # and store them into a list variable
        skills = request.query_params.getlist('skills', '')
        
        # if we wish to make a query using multiple conditions from a list
        query = Q()
        for skill in skills:
            query.add(Q(skill_set__name=skill), Q.OR)

        job_skills = JobPostSkillSet.objects.filter(query)
        # SELECT * FROM jobpostskillset WHERE (skill_set__name='node') OR (skill_set__name='postgresql'))

        job_posts = JobPost.objects.filter(
            id__in=[job_skill.job_post.id for job_skill in job_skills]
        )

        if job_posts.exists():
            serializer = JobPostSerializer(job_posts, many=True)
            return Response(serializer.data)
  • Our final output will be serialized data of JobPost instance 1 and 3:
[
    {
        "id": 1,
        "position_type": "permanent",
        "company": {
            "id": 1,
            "company_name": "company1"
        },
        "job_description": "jd1",
        "salary": 100000000,
        "skiilsets": [
            "postgresql"
        ]
    },
    {
        "id": 3,
        "position_type": "permanent",
        "company": {
            "id": 3,
            "company_name": "company3"
        },
        "job_description": "jd3",
        "salary": 100000000,
        "skiilsets": [
            "mysql",
            "node"
        ]
    }
]

 

  •  JobType and Company are both in a many-to-one relationship with JobPost.
    • When creating model instances, we must save its Foreign Key object instances in a separate manner after the request data has been validated.
class JobPostView(APIView):

    def post(self, request):
        job_type = request.data.get("job_type", None)
        company_name = request.data.get("company_name", None)
        
        try: 
            job_type_instance = JobType.objects.get(job_type=job_type)
        except JobType.DoesNotExist:
            return Response({"message": "Please enter a valid job type"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            Company.objects.get(company_name=company_name)
        except Company.DoesNotExist:
            company_instance = Company(company_name=company_name).save()

        job_serializer = JobPostSerializer(data=request.data)
        if job_serializer.is_valid():
            job_serializer.save(company=company_instance, job_type=job_type_instance)
            return Response(status=status.HTTP_200_OK)

        return Response(job_serializer.errors, status=status.HTTP_400_BAD_REQUEST)