Skip to content

Commit

Permalink
Fixed a number of problems with PUT/PATCH methods for TaskSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita Manovich committed Feb 6, 2019
1 parent dff3b51 commit a4054a0
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 6 deletions.
18 changes: 18 additions & 0 deletions cvat/apps/engine/migrations/0028_auto_20190206_1620.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2019-02-06 13:20

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('engine', '0027_auto_20190206_1318'),
]

operations = [
migrations.AlterField(
model_name='task',
name='segment_size',
field=models.PositiveIntegerField(default=0),
),
]
3 changes: 2 additions & 1 deletion cvat/apps/engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import shlex
import csv
import os
import sys


class StatusChoice(str, Enum):
Expand Down Expand Up @@ -55,7 +56,7 @@ class Task(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now_add=True)
overlap = models.PositiveIntegerField(default=0)
segment_size = models.PositiveIntegerField()
segment_size = models.PositiveIntegerField(default=0)
z_order = models.BooleanField(default=False)
flipped = models.BooleanField(default=False)
image_quality = models.PositiveSmallIntegerField()
Expand Down
79 changes: 74 additions & 5 deletions cvat/apps/engine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ class Meta:
'values')

def to_internal_value(self, data):
data['values'] = '\n'.join(data['values'])
attribute = data.copy()
attribute['values'] = '\n'.join(data['values'])
return attribute

def to_representation(self, instance):
attribute = super().to_representation(instance)
Expand Down Expand Up @@ -115,8 +117,52 @@ def update(self, instance, validated_data):

return instance

class WriteOnceMixin:
"""Adds support for write once fields to serializers.
To use it, specify a list of fields as `write_once_fields` on the
serializer's Meta:
```
class Meta:
model = SomeModel
fields = '__all__'
write_once_fields = ('collection', )
```
Now the fields in `write_once_fields` can be set during POST (create),
but cannot be changed afterwards via PUT or PATCH (update).
Inspired by http://stackoverflow.com/a/37487134/627411.
"""

def get_extra_kwargs(self):
extra_kwargs = super().get_extra_kwargs()

# We're only interested in PATCH/PUT.
if 'update' in getattr(self.context.get('view'), 'action', ''):
return self._set_write_once_fields(extra_kwargs)

return extra_kwargs

def _set_write_once_fields(self, extra_kwargs):
"""Set all fields in `Meta.write_once_fields` to read_only."""
write_once_fields = getattr(self.Meta, 'write_once_fields', None)
if not write_once_fields:
return extra_kwargs

if not isinstance(write_once_fields, (list, tuple)):
raise TypeError(
'The `write_once_fields` option must be a list or tuple. '
'Got {}.'.format(type(write_once_fields).__name__)
)

for field_name in write_once_fields:
kwargs = extra_kwargs.get(field_name, {})
kwargs['read_only'] = True
extra_kwargs[field_name] = kwargs

return extra_kwargs

class TaskSerializer(serializers.ModelSerializer):
class TaskSerializer(WriteOnceMixin, serializers.ModelSerializer):
labels = LabelSerializer(many=True, source='label_set', partial=True)
segments = SegmentSerializer(many=True, source='segment_set', read_only=True)
image_quality = serializers.IntegerField(min_value=0, max_value=100,
Expand All @@ -129,13 +175,12 @@ class Meta:
'segment_size', 'z_order', 'flipped', 'status', 'labels', 'segments',
'image_quality')
read_only_fields = ('size', 'mode', 'created_date', 'updated_date',
'overlap', 'status', 'segment_size')
'status')
write_once_fields = ('overlap', 'segment_size')
ordering = ['-id']

def create(self, validated_data):
labels = validated_data.pop('label_set')
if not validated_data.get('segment_size'):
validated_data['segment_size'] = 0
db_task = Task.objects.create(size=0, **validated_data)
for label in labels:
attributes = label.pop('attributespec_set')
Expand All @@ -154,6 +199,30 @@ def create(self, validated_data):

return db_task

def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.owner = validated_data.get('owner', instance.owner)
instance.assignee = validated_data.get('assignee', instance.assignee)
instance.bug_tracker = validated_data.get('bug_tracker',
instance.bug_tracker)
instance.z_order = validated_data.get('z_order', instance.z_order)
instance.flipped = validated_data.get('flipped', instance.flipped)
instance.image_quality = validated_data.get('image_quality',
instance.image_quality)
labels = validated_data.get('label_set')
for label in labels:
attributes = label.pop('attributespec_set')
(db_label, _) = Label.objects.get_or_create(task=instance, **label)
for attr in attributes:
(db_attr, created) = AttributeSpec.objects.get_or_create(
label=db_label, **attr)




return instance


class UserSerializer(serializers.ModelSerializer):
groups = serializers.SlugRelatedField(many=True,
slug_field='name', queryset=Group.objects.all())
Expand Down
3 changes: 3 additions & 0 deletions cvat/apps/engine/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer

def get_serializer_class(self):
return self.serializer_class

@action(detail=True, methods=['GET'], serializer_class=JobSerializer)
def jobs(self, request, pk):
queryset = Job.objects.filter(segment__task_id=pk)
Expand Down

0 comments on commit a4054a0

Please sign in to comment.