Django - item created by logged user ViewSet

What I want: A logged user creates an item which is stored in the database with that user ForeignKey.

I have 2 codes: both should do the same thing (create item linked to current user), but one of them works and the other one doesn't. GET requests work, I'm focusing on POST requests.

First code (POST working):

class UserPortfolio(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)

    name = models.CharField(max_length=200, blank=False, null=False)
    slug = models.CharField(max_length=200, blank=False, null=False)

class UserPortfolioSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserPortfolio
        exclude = ('id', 'user')

class UserPortfolioViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.UserPortfolioSerializer
    queryset = UserPortfolio.objects.all()
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return UserPortfolio.objects.filter(user=self.request.user).order_by('last_updated')

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Second code (POST not working):

class ExchangeConnection(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    portfolios = models.ManyToManyField(UserPortfolio, blank=True)
    exchange = models.ForeignKey(Exchange, on_delete=models.CASCADE)
    
    last_updated = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'exchange_connection'
        verbose_name_plural = "Exchange Connection"

    def __str__(self):
        return self.user.username

class ExchangeConnectionSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = ExchangeConnection
        fields = '__all__'
        expandable_fields = {'exchange': ExchangeSerializer,
                             'portfolios': (UserPortfolioSerializer, {'many': True})}

class UserExchangeConnectionsViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.ExchangeConnectionSerializer
    queryset = ExchangeConnection.objects.all()
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return ExchangeConnection.objects.filter(user=self.request.user).order_by('last_updated')

    @action(
        detail=True,
        methods=['post'],
        url_path='create-exchange-connection',
    )
        def post(self, request):
            serializer = serializers.ExchangeConnectionPostSerializer(user=request.user)
        if serializer.is_valid():
            serializer.save()
            return Response({'status': 'Exchange created.'})
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

My urls:

from django.urls import path, include
from api_v1.exchanges.views import UserPortfolioViewSet, UserExchangeConnectionsViewSet, ExchangesViewSet
from rest_framework.routers import DefaultRouter


# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'portfolios', UserPortfolioViewSet)
router.register(r'exchange-connections', UserExchangeConnectionsViewSet)
router.register(r'exchanges', ExchangesViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

The error I get when I POST is:

TypeError: post() got an unexpected keyword argument 'pk'

Upvotes: 0

Views: 47

Answers (2)

iklinac
iklinac

Reputation: 15738

As documented

Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the detail argument to True or False. The router will configure its URL patterns accordingly. e.g., the DefaultRouter will configure detail actions to contain pk in their URL patterns.

so you should set detail=False

@action(
        detail=False,
        methods=['post'],
        url_path='create-exchange-connection',
    )

Upvotes: 0

Hitman
Hitman

Reputation: 81

@action(
        detail=True,
        methods=['post'],
        url_path='create-exchange-connection',
    )
    def post(self, serializer):
        serializer = serializers.ExchangeConnectionPostSerializer(user=self.request.user)
        if serializer.is_valid():
            serializer.save()
            return Response({'status': 'Exchange created.'})
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

you have used detail = True in the action, which means you are making a retrieve request for a particular object, so you must pass a lookup_field value which is by default pk.

If you just want to make a create request then you should set detail=False.

Updated code.

@action(
        detail=False,
        methods=['post'],
        url_path='create-exchange-connection',
    )
    def post(self, serializer):
        serializer = serializers.ExchangeConnectionPostSerializer(user=self.request.user)
        if serializer.is_valid():
            serializer.save()
            return Response({'status': 'Exchange created.'})
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Upvotes: 2

Related Questions