I have one class representing a Job, one class representing a Tag, which describes a Job and then I have a class making a relationship (junction table) between them, so a Job can be described by multiple Tags:
class JobTag(models.Model):
job = models.ForeignKey(Job, unique=False, related_name='jobtags')
tag = models.ForeignKey(Tag, unique=False, related_name='Tag_For_Job')
created_time = models.DateTimeField(auto_now_add = True)
modified_time = models.DateTimeField(auto_now = True)
class Meta:
unique_together = ('job', 'tag',)
def __unicode__(self):
return 'Tag '+self.tag.name +' for job '+ self.job.name
Then I have serializers:
class TagSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tag
fields = ('url','name','badge_pic')
read_only_fields = ('name','badge_pic')
class JobTagSerializer(serializers.HyperlinkedModelSerializer):
tag = TagSerializer()
class Meta:
model = JobTag
fields = ('tag',)
depth=1
class JobSerializer(serializers.HyperlinkedModelSerializer):
jobtags=JobTagSerializer(many=True)
class Meta:
model = Job
fields = ('url','name', 'employer','jobtags','description')
read_only_fields = ('employer',)
So the http response on GET request is:
{
"url": "http://127.0.0.1:8000/api/jobs/2/",
"name": "Odprac mi sneh",
"employer": "http://127.0.0.1:8000/api/users/4/",
"jobtags": [
{
"tag": {
"url": "http://127.0.0.1:8000/api/tags/2/",
"name": "Odhadzovanie snehu",
"badge_pic": "http://127.0.0.1:8000/media/pictures/tags/0005.jpg"
}
}
],
"description": "blablabla"
}
My question is pretty much obvious, how can I create a job instance and persist them with related JobTags with one POST http request?
I tried to repeat this method http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations.
class JobSerializer(serializers.HyperlinkedModelSerializer):
jobtags=JobTagSerializer(many=True)
class Meta:
model = Job
fields = ('url','name', 'employer','jobtags','description')
read_only_fields = ('employer',)
def create(self, validated_data):
jobtag_data = validated_data.pop('jobtags')
job = Job.objects.create(**validated_data)
JobTag.objects.create(job=job, **jobtag_data)
return job
But it returned "create() argument after ** must be a mapping, not list" error, so what should request.data json looks like?
Or this approach cannot be used in my case and should I do something completly different?
I would appreciate any help.
Edit
if i try to access the list:
def create(self, validated_data):
jobtag_data = validated_data.pop('jobtags')
job = Job.objects.create(**validated_data)
JobTag.objects.create(job=job, **jobtag_data[0])
return job
I gen another error:"Cannot assign "OrderedDict()": "JobTag.tag" must be a "Tag" instance." So im guessing my posted json is in wrong format? I tried topost data this way:
{
"name": "Odprac mi sneh",
"jobtags": [
{
"tag": {
"url": "http://127.0.0.1:8000/api/tags/2/"
}
}
],
"description": "veela sneu nemam ruky makam makam makamam",
}
I believe you should provide the id
and not the url
of each tag in your POST data, like so:
{
"name": "Odprac mi sneh",
"tags": [
{
"id": 2
},
{
"id": 3
}
],
"description": "veela sneu nemam ruky makam makam makamam"
}
Then, in your create
method you should be able to iterate over the tags:
def create(self, validated_data):
tag_data = validated_data.pop('tags')
job = Job.objects.create(**validated_data)
for tag in tag_data:
JobTag.objects.create(job=job, tag_id=tag["id"])
return job
If anyone else is facing this, the most suitable solution I figured out, so that the hyperlinked serialisation of jobtags was used when object is created and nested serialisation was used as an output is this:
I wrote serializers for each of these cases, for serialisation of data sent to client:
class JobTagNestedSerializer(serializers.HyperlinkedModelSerializer):
tag = TagSerializer()
class Meta:
model = JobTag
fields = ('tag',)
depth=1
class JobNestedSerializer(serializers.HyperlinkedModelSerializer):
jobtags=JobTagNestedSerializer(many=True,read_only=True)
class Meta:
model = Job
fields = ('url','name', 'employer','jobtags','description')
read_only_fields = ('employer',)
and for creating new Job, so for data sent from client to DRF:
class JobTagSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = JobTag
fields = ('tag',)
class JobCreateSerializer(serializers.HyperlinkedModelSerializer):
jobtags=JobTagSerializer(many=True,required=False)
class Meta:
model = Job
fields = ('url','name', 'employer','jobtags','description')
read_only_fields = ('employer',)
def create(self, validated_data):
tag_data = validated_data.pop('jobtags')
job = Job.objects.create(**validated_data)
for tag in tag_data:
d=dict(tag)
JobTag.objects.create(job=job, tag_id=d['tag'].pk)
return job
So DRF is expecting the POST json from client to looks like:
{
"name": "Odprac mi sneh",
"employer": "http://127.0.0.1:8000/api/users/4/",
"jobtags": [
{
"tag": "http://127.0.0.1:8000/api/tags/2/"
},
{
"tag": "http://127.0.0.1:8000/api/tags/5/"
}
],
"description": "veela sneu nemam ruky makam makam makamam"
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With