4

I'm currently starting a simple Task App and I'm using Django 2.0.7, DRF 3.8.2 and drf-nested-routes 0.90.2

I have these models :

class Client(TimeStampedModel):
    """
    This model describes a client for the railroader. It can be created by the manager in the back office
    We have at least one internal Client, which is Seelk, for internal projects
    """
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(null=True)
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return "{} : {}".format(self.name, self.description)

class Project(TimeStampedModel):
    """
    This model represents a project for a client, which we are gonna track actions on
    """
    client = models.ForeignKey(
        'railroader.Client', on_delete=models.PROTECT, related_name='projects')
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(null=True)
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return "{} for client {}".format(self.name, self.client.name)

So, following the documentation of drf-nested-routers, I set up my serializers like this :

class ClientSerializer(serializers.ModelSerializer):
    class Meta:
        model = Client
        fields = ("id", "name", "description", "is_active", "projects")


class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = ("id", "name", "description", "is_active")

And my viewsets like this :

class ClientViewset(viewsets.ModelViewSet):
    serializer_class = ClientSerializer
    permission_classes = (permissions.IsAuthenticated, AccountPermission)

    def get_queryset(self):
        queryset = Client.objects.all()
        is_active = self.request.query_params.get("is_active")
        if is_active:
            queryset = queryset.filter(is_active=is_active)
        return queryset


class ProjectViewset(viewsets.ModelViewSet):
    serializer_class = ProjectSerializer
    permission_classes = (permissions.IsAuthenticated, AccountPermission)

    def get_queryset(self):
        queryset = Project.objects.filter(client=self.kwargs["client_pk"])
        is_active = self.request.query_params.get("is_active")
        if is_active:
            queryset = queryset.filter(is_active=is_active)
        return queryset

And finally, my urls like so :

router = routers.SimpleRouter()
router.register(r"clients", viewsets.ClientViewset, base_name="clients")

projects_router = routers.NestedSimpleRouter(router, r"clients", lookup="client")
projects_router.register(r"projects", viewsets.ProjectViewset, base_name="projects")
urlpatterns = [
    re_path(r"^", include(router.urls)),
    re_path(r"^", include(projects_router.urls))
]

With this setup, I'm able to have the desired nested routes, but I can't have my routes to create a new object if I post on a nested route.

I've seen an issue on the github speaking about it, but as it was 2 years ago, I wonder if anyone knows how to do it.

Thanks in advance.

a-coruble
  • 411
  • 4
  • 15

1 Answers1

7

Found out I just forgot that returning an instance in DRF create method of the serializer would not create the object in base. At the end I have this serializer :

class ProjectSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        client = Client.objects.get(pk=self.context["view"].kwargs["client_pk"])
        validated_data["client"] = client
        return Project.objects.create(**validated_data)

    class Meta:
        model = Project
        fields = ("id", "name", "description", "is_active")
a-coruble
  • 411
  • 4
  • 15
  • Btw, it should work if you add 'client' to the Meta.fields tuple. In that case, you should not need to set it in the create and it should be handled for you automatically. – Enthusiast Martin Jul 16 '18 at 07:55
  • @EnthusiastMartin I did it, but the serializer then asks for it in the validation and I don't want to have to manually post the id of the client, as I'm already in his nested urls, it should be automatic. Just adding the field in the serializer doesn't work for creating without sending it. Thanks for the help though ! – a-coruble Jul 16 '18 at 09:05
  • ah, ok. i thought that you were posting it. my bad. In that case, perhaps overriding perform_create in your view and feeding the client id into the serializer's save method would be solution to consider as well. – Enthusiast Martin Jul 16 '18 at 09:17
  • @EnthusiastMartin Hmm, this one's interesting, I will test it too. In term of good practice, I think both are okay. I'll let you know about your implementation. – a-coruble Jul 16 '18 at 12:54