Skip to content

Conversation

@kernelshard
Copy link
Contributor

@kernelshard kernelshard commented Nov 6, 2025

Fixes #9596

This PR adds validation to detect when API policy decorators (@permission_classes, @renderer_classes, etc.) are applied in the wrong order with @api_view.

Problem

When policy decorators are applied after @api_view (i.e., placed above it in code), the policy settings are silently ignored. This happens because:

  • If policy decorators are applied after @api_view, they set attributes on the already-created view function, not the original function

Solution

Added a _check_decorator_order() helper that:

  • Detects when a decorator is applied to a view function (result of APIView.as_view())
  • Raises a clear TypeError with the correct decorator order
  • Applied to all 9 policy decorators

Tests

Added 9 comprehensive tests covering all policy decorators to ensure they properly validate ordering.

Raise TypeError when API policy decorators (@permission_classes,
@renderer_classes, etc.) are applied after @api_view instead of before it.

Fixes encode#9596
@auvipy auvipy requested a review from Copilot November 6, 2025 17:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds validation to ensure API policy decorators are applied in the correct order relative to the @api_view decorator. The feature prevents a common mistake where developers apply policy decorators after @api_view, which silently fails to work as expected.

  • Introduced _check_decorator_order() helper function to detect incorrect decorator ordering
  • Added order validation to all nine API policy decorators
  • Comprehensive test coverage for all affected decorators

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
rest_framework/decorators.py Added _check_decorator_order() validation function and integrated it into all API policy decorators to raise TypeError when applied after @api_view
tests/test_decorators.py Added nine test cases to verify that each API policy decorator raises TypeError when incorrectly applied after @api_view

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 99 to 100
f" @api_view(['GET'])\n"
f" @{decorator_name}(...)\n"
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message shows the decorators in the wrong order. When decorators are stacked, they are applied bottom-to-top, so @api_view should be on top and @{decorator_name} should be below it. The example currently shows:

@api_view(['GET'])
@{decorator_name}(...)
def my_view(request):
    ...

But this is the incorrect order being warned against. It should be:

@{decorator_name}(...)
@api_view(['GET'])
def my_view(request):
    ...
Suggested change
f" @api_view(['GET'])\n"
f" @{decorator_name}(...)\n"
f" @{decorator_name}(...)\n"
f" @api_view(['GET'])\n"

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator needs to go below @api_view, just as the docs show—this way, DRF reads the attribute properly and permissions work.
Our tests confirm the documented order is correct; error is raised only if you swap them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will get back to you on saturday morning

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, Copilot is wrong here, but that might be a symptom that "applied before" is a bit ambiguous in this case. The docs are actually using the term "after":

These must come after (below) the @api_view decorator

The docs refer to the order the decorators appear in the code: as we read top to bottom the order is swapped vs how they are applied by the code (bottom to top).

Understanding how decorators are applied by the language is important, but less experience people might not know it, and therefore won't understand the error message.

I'd rather make the language of the error message closer to what we have in the docs.

Comment on lines 99 to 100
f" @api_view(['GET'])\n"
f" @{decorator_name}(...)\n"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, Copilot is wrong here, but that might be a symptom that "applied before" is a bit ambiguous in this case. The docs are actually using the term "after":

These must come after (below) the @api_view decorator

The docs refer to the order the decorators appear in the code: as we read top to bottom the order is swapped vs how they are applied by the code (bottom to top).

Understanding how decorators are applied by the language is important, but less experience people might not know it, and therefore won't understand the error message.

I'd rather make the language of the error message closer to what we have in the docs.

if hasattr(func, 'cls') and issubclass(func.cls, APIView):
raise TypeError(
f"@{decorator_name} must be applied before @api_view. "
f"The correct order is:\n\n"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need the f prefix on lines where there is nothing to interpolate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@browniebroke Thanks for the review! I've updated the error message to address it.

The error message now:

@permission_classes must come after (below) the @api_view decorator. The correct order is:

@api_view(['GET'])
@permission_classes(...)
def my_view(request):
    ...

- Change 'must be applied before' to 'must come after (below) the' to match DRF docs language
- Fix decorator order in example to show @api_view first, then policy decorator below
- Remove unnecessary f-string prefixes from non-interpolated lines
- Update all test assertions to match new error message wording

Addresses feedback from @browniebroke in PR encode#9821
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Views decorators ordering

3 participants