简体   繁体   中英

Handling related models in DRF and getting the right swagger docs for it

So I'm having a bit of trouble with something I was trying to do.

Basically, I have these models:

class Package(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=255, null=False, blank=False)
    contact = models.BooleanField(default=False)
    downloaded = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True)

class Item(models.Model):
          […. all the atributes of the item model….]

class PackageItems(models.Model):
    package = models.ForeignKey(Package, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)

and now I am trying to make an endpoint that allows my users to add “package” and add a pre-existing item in an item model to that newly created package. One package can of course have many items.

SO I wrote a Package serializer then added a SerializerMethodField that allows one to do a get operation on any item that a given package contains. The method makes a call to different serializer. Here's the code for both the serializers:

class PackageItemsSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = PackageItems
        fields = ('package', 'item')

class PackagesSerializer(serializers.ModelSerializer):
    """
    Creation only requires a title
    """

    package_items = serializers.SerializerMethodField(read_only=True)

    @swagger_serializer_method(serializer_or_field=packageItemsSerializer)
    def get_package_items(self, obj):
        packageItems = PackageItems.objects.all().filter(package=obj.id)
        return PackageItemsSerializer(packageItems, many=True, context={'request': self.context['request']}).data

    def create(self, validated_data):
        package = super(packagesSerializer, self).create(validated_data)
        return package

    class Meta:
        model = packages
        fields = ('id', 'user', 'title', 'contact', 'downloaded', 'created', 'package_items')
        read_only_fields = [ 'user', 'contact', 'downloaded', 'package_items’]

Now for my views, I have decided to do this:

class PackagesViewSet(viewsets.ModelViewSet):

    queryset = Packages.objects.all()
    serializer_class = PackagesSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        if  getattr(self, 'swagger_fake_view', False):
            return Packages.objects.none()

        return Packages.objects.filter(user=self.request.user).order_by('id')

    def perform_create(self, serializer):
        # sending default values for user, contact and downloaded
        serializer.save(user=self.request.user, contact=False, downloaded=False)

    def partial_update(self, request, *args, **kwargs):
        response_with_updated_instance = super(PackagesViewSet, self).partial_update(request, *args, **kwargs)
        return response_with_updated_instance

    @action(methods=['post', 'delete'], detail=True, serializer_class=PackageItemsSerializer)
    def item(self, request, pk=None):
        package = self.get_object()
        serializer = self.get_serializer_class()(data=request.data)
        if serializer.is_valid():
            serializer.save(package=package)
            return Response(serializer.data)
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST)
        if request.method == 'delete':
            serializer.delete(package=package)

This is what works with this: I get these routes:

[get/post] for /api/packages/

[all methods] for /api/packages/{id}

[post/delete] for /api/packages/{id}/item

I get the right routes, but for one, Swagger gives me the wrong models for post:

{
  "title": "string",
  "package_items": {
    "items": 0
  }
}

Not sure why it's expecting this read_only field to be entered (which is a serializermethodfield as well) when I do a post to /api/packages .

Similarly, these fields show up for PUT under /api/packages/{id} .

For the nested "item" route, the POST works well, but the DELETE doesn't. The POST route allows me to enter an item ID and adds it to the given package ID under /api/packages/{id}/item route. But delete doesn't allow me to enter an item ID to delete.

So I'm thinking my approach to the delete method for the nested ITEM is completely wrong.

I'm new to DRF/django, trying to validate if I'm going in the right direction with this.

How do I get drf-yasg to get me the right model for the HTTP verbs like POST for example? What's the right way to delete the nested Item?

Am I approaching this problem right? Is there another way to do this more efficiently?

If someone can help me out to answer those questions, it would be really appreciated :)

Thanks in advance folks.

Instead of nesting serializer in serializer.SerializerMethodField directly, nest it like:

package_item = PackageItemsSerializer(source="package_set", many=True, read_only=True)

try this

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM