0

I have the following simple models for a todo list:

class TodoList(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=255)


class Todo(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    todo_title = models.CharField(max_length=64)
    todo_body = models.TextField()
    completed = models.BooleanField(default=False)
    list = models.ForeignKey(TodoList, on_delete=models.CASCADE, related_name='messages')

What I am trying to do is set up a nested route using drf-nested-routers. E.g.:

/api/v1/todo-lists/                           <- List Todo Lists
/api/v1/todo-lists/{LIST_ID}/                 <- CRUD a Todo list
/api/v1/todo-lists/{LIST_ID}/todos/           <- List todos for a particular list
/api/v1/todo-lists/{LIST_ID}/todos/{TODO_ID}/ <- CRUD for a particular todo

I've got a Todo Serializer:

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'todo_title', 'todo_body', 'completed', 'list',)
        read_only_fields = ('id', 'list',)

And a TodoByList Viewset:

class TodoByListViewSet(mixins.CreateModelMixin,
                        mixins.ListModelMixin,
                        viewsets.GenericViewSet):
    serializer_class = TodoSerializer
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        return Todo.objects.filter(list_id=self.kwargs['todolist_pk'])

    def create(self, request, todolist_pk=None):
        todo_list = get_object_or_404(TodoList, pk=todolist_pk)
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

The list view works great, however for create I am in a bit of a catch-22. The list parameter for my Todo model is required (rightfully so), and thus perform_create doesn't work since list is not set. But if I remove list from the read_only_fields in my serializer, .is_valid fails since I am not passing the list id in with my request data. What I need to do is inject the todo_list instance retrieved from the url parameters, but I am unsure as to how this can be done.

MarkD
  • 4,864
  • 5
  • 36
  • 67

1 Answers1

0

You need to remove list from read_only_fields in your serializer. Then you need pass todo_list id to serializer. For example:

def create(self, request, todolist_pk=None):
    todo_list = get_object_or_404(TodoList, pk=todolist_pk)
    dict_data = dict(request.data)
    dict_data['list'] = todo_list.id
    serializer = self.get_serializer(data=dict_data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)