Search code examples
djangodjango-rest-frameworkdjango-urlsdjango-rest-viewsets

django rest framework using action decorator to add path to view financial statement in url


I need the following URLs to work where ticker would stand for a stock ticker such as AAPL or AMZN, and is stands for income_statement.

localhost:8000/stocks/ localhost:8000/stocks/<TICKER>/ localhost:8000/stocks/<TICKER>/is/

in the views.py file below I am using a viewset which and router which automatically configures the first two urls above, and for the third url I am using the action decorator with methods=['get'] and url_path="is" to achieve the localhost:8000/stocks/<TICKER>/is/ path.

the third URL is configured, but I get a key error for ticker when entering the following url in the browser: localhost:8000/stocks/AAPL/is/

what am I doing wrong here, I believe the error is in the views.py get_income_statement function?

models.py

class Stock(models.Model):

    id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    ticker = models.CharField(max_length=10, unique=True, primary_key=True)
    slug = models.SlugField(default="", editable=False)

    def save(self, *args, **kwargs):
        value = self.ticker
        self.slug = slugify(value, allow_unicode=True)
        super().save(*args, **kwargs)

    def __str__(self):
        return self.ticker

    class Meta:
        verbose_name = "stock"
        verbose_name_plural = "stocks"
        ordering = ["ticker"]

class IncomeStatement(models.Model):
    ticker = models.ForeignKey(
        Stock, on_delete=models.CASCADE, related_name="income_statements"
    )

    date = models.DateField(default=datetime.date.today)
    PERIODICITY_CHOICES = [("ANNUAL", "ANNUAL"), ("QUARTERLY", "QUARTERLY")]
    periodicity = models.CharField(
        max_length=10, choices=PERIODICITY_CHOICES, default="annually"
    )
    net_income_continuous_operations = models.DecimalField(
        max_digits=20, decimal_places=2

views.py

class StockViewSet(viewsets.ModelViewSet):
    queryset = Stock.objects.all()
    serializer_class = StockSerializer
    # lookup_field = "slug"

    @action(detail=True, methods=["get"], url_path="is", url_name="is")
    def get_income_statement(self, request, *args, **kwargs):
        income_statement = self.queryset.get(ticker=kwargs["ticker"]).select_related(
            "income_statements"
        )
        serializer = IncomeStatementSerializer(data=income_statement)
        if serializer.is_valid():
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

urls.py

router = DefaultRouter()
router.register(r"stocks", views.StockViewSet)
urlpatterns = router.urls

Solution

  • Set lookup_field = "ticker"

    class StockViewSet(viewsets.ModelViewSet):
        # rest of your code
        lookup_field = "ticker"

    Update-1

    class StockViewSet(viewsets.ModelViewSet):
        queryset = Stock.objects.all()
        serializer_class = StockSerializer
        lookup_field = "ticker"
    
        @action(detail=True, methods=["get"], url_path="is", url_name="is")
        def get_income_statement(self, request, *args, **kwargs):
            is_qs = IncomeStatement.objects.filter(ticker=self.get_object())
            serializer = IncomeStatementSerializer(is_qs, many=True)
            return Response(serializer.data)