I am using Django Rest Framework to build a job board, while creating an option for users to create job postings, I receive the error
AssertionError at /jobs/create
The
.create()
method does not support writable nested fields by default. Write an explicit.create()
method for serializerafritechjobsapi.serializers.PostAJobSerializer
, or setread_only=True
on nested serializer fields.
Reading the documentation for writing nested serializers - Serializer relations - Django REST framework I see that there is an example of how to tweak serializers to define a ‘create method’.
In the example seen on DRF documentation and other examples I’ve seen on stackoverflow - python - django - making .create() support writable nested fields? - Stack Overflow and Python Django Rest Framework: The `.create()` method does not support writable nested fields by default - Stack Overflow and python - Serializers.validated_data fields got changed with source value in DRF - Stack Overflow they are trying to create one nested serialization however I am working with many as seen below;
VIEWS.PY
@api_view(['POST'])
def post_a_job(request):
if request.method == 'POST':
post_a_job_serializer = PostAJobSerializer(data=request.data)
if post_a_job_serializer.is_valid():
post_a_job_serializer.save()
return Response(post_a_job_serializer.data, status=status.HTTP_201_CREATED)
return Response(post_a_job_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
URLS.PY
path("jobs/create", views.post_a_job, name="post_a_job"),
MODELS.PY
class JobSkills(models.Model):
class SkillsChoices(models.TextChoices):
DJANGO = 'DJ', 'Django'
REACT = 'RT', 'React'
#TODOmore things to be added
#TODOskills options should change based on the selected category
title = models.CharField(max_length=20, choices=SkillsChoices.choices, default=SkillsChoices.DJANGO)
category = models.ManyToManyField(Category)
def __str__(self):
return self.title
class JobLocations(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class JobType(models.Model):
class JobTypeChoices(models.TextChoices):
CONTRACT = 'CT', 'Contract'
FULLTIME = 'FT', 'FullTime'
FREELANCE = 'FL', 'Freelance'
INTERNSHIP = 'IP', 'Internship'
PARTTIME = 'PT', 'Parttime'
job_type_choices = models.CharField(max_length=2, choices=JobTypeChoices.choices, default=JobTypeChoices.FULLTIME)
class JobLevel(models.Model):
class JobLevelChoices(models.TextChoices):
STUDENT = 'ST', 'Student'
INTERN = 'IN', 'Intern'
ENTRYLEVEL = 'EL', 'Entrylevel'
MIDLEVEL = 'ML', 'Midlevel'
SENIORLEVEL = 'SL', 'Seniorlevel'
COFOUNDER = 'CF', 'Cofounder'
DIRECTOR = 'DC', 'Director'
MANAGER = 'MG', 'Manager'
CEO = 'CEO', 'ChiefExecutiveOfficer'
CTO = 'CTO', 'ChiefTechnologyOfficer'
CMO = 'CMO', 'ChiefMarketingOfficer'
CFO = 'CFO', 'Chief Financial Officer'
COO = 'COO', 'Chief Operating Officer'
job_level_choices = models.CharField(max_length=3, choices=JobLevelChoices.choices, default=JobLevelChoices.ENTRYLEVEL)
class PostAJob(models.Model):
job_title = models.CharField(max_length=200)
job_category = models.ForeignKey(Category, on_delete=models.CASCADE)
job_skills = models.ForeignKey(JobSkills, on_delete=models.SET_NULL, null=True, blank=True)
job_salary_range = models.IntegerField(blank=True)
job_description = models.TextField()
job_type = models.ForeignKey(JobType, on_delete=models.CASCADE)
job_location = models.ForeignKey(JobLocations, on_delete=models.CASCADE, default='')
job_level = models.ForeignKey(JobLevel, on_delete=models.CASCADE)
job_application_link = models.URLField(max_length=200)
company_name = models.CharField(max_length=200)
company_hq = models.CharField(max_length=200)
company_logo = models.ImageField()
companys_website = models.URLField(max_length=200)
company_contact_email = models.EmailField(max_length=200, null=True, blank=True)
company_description = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
# created_by = models.ForeignKey(Profile, on_delete=models.CASCADE)
def __str__(self):
return self.job_title
SERIALIZERS.PY
class PostAJobSerializer(serializers.ModelSerializer):
job_category = CategorySerializer(many=True)
job_skills = JobSkillsSerializer(many=True)
job_type = JobTypeSerializer()
job_location = JobLocationsSerializer(many=True)
job_level = JobLevelSerializer(many=True)
created_by = ProfileSerializer(read_only=True)
class Meta:
model = PostAJob
fields = ['id', 'job_title', 'job_category', 'job_skills', 'job_salary_range', 'job_description', 'job_type', 'job_location', 'job_level', 'job_application_link', 'company_name', 'company_hq', 'companys_website', 'company_contact_email', 'company_description', 'date_created', 'date_updated']
def create(self, validated_data):
job_category = validated_data.pop('job_category')
job_skills = validated_data.pop('job_skills')
job_type = validated_data.pop('job_type')
job_location = validated_data.pop('job_location')
job_level = validated_data.pop('job_level')
post_a_job = PostAJob.objects.create(**validated_data)
#TODO: DON'T KNOW WHAT
return super().create(validated_data)
I don’t know how to handle the create method with multi-nested validation that needs to happen (job_category, job_skills, job_skills, job_type, job_location, job_level) in order for a user to create a job posting and would like some guide on this.