-
Notifications
You must be signed in to change notification settings - Fork 273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix inheritance bugs with @extend_schema_view(). #554
Conversation
Codecov Report
@@ Coverage Diff @@
## master #554 +/- ##
==========================================
- Coverage 98.77% 98.76% -0.01%
==========================================
Files 58 58
Lines 6508 6540 +32
==========================================
+ Hits 6428 6459 +31
- Misses 80 81 +1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tfranzel This was causing quite a mess in the code that I've been converting. I hope this all makes sense?
@extend_schema_view( | ||
list=extend_schema(exclude=True), | ||
retrieve=extend_schema(description='overridden description for child only'), | ||
extended_action=extend_schema(responses={200: {'type': 'string', 'pattern': r'^[0-9]{4}(?:-[0-9]{2}){2}$'}}), | ||
raw_action=extend_schema(summary="view raw action summary"), | ||
) | ||
class ZViewSet(XViewSet): | ||
@extend_schema(tags=['child-tag']) | ||
@action(detail=False, methods=['GET']) | ||
def raw_action(self, request): | ||
return Response('2019-03-01') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tests the child with @extend_schema_view
inheriting from a parent with @extend_schema_view
scenario.
In this case:
- We want
list
to be removed from the schema only for the child. - We want
retrieve
to havedescription
overridden only for the child, but retaintags
from the parent. - We want
extended_action
to haveresponses
overridden only for the child, but retaindescription
from the parent. - We want
raw_action
to override thetags
via@extend_schema
in the child andsummary
via@extend_schema_view
on the child without affecting the parent.- The only thing I'm unsure about here is that we don't get the
description
from the parent's@extend_schema_view
, but we would get thetags
from the@extend_schema
on the parent if we weren't overriding it. Perhaps this needs to be tweaked to ensure that we still getdescription
?
- The only thing I'm unsure about here is that we don't get the
Hi @tfranzel 👋🏻 Did you have any thoughts on this one? |
hey @ngnpope! first of all thank you for tackling this issue! Somehow I never got around doing it. I had already set aside time today to review this in detail and I didn't want to give a partial review. As you likely noticed, the mechanics are tricky and I'd like to make sure that all corner cases are covered. On first sight it looks good to me.
|
34cd906
to
d8bbb22
Compare
I know the feeling! I tackled it out of necessity.
💯
I've had a go at producing a smaller regression test. I attempted to avoid defining a custom action by using |
Oh boy, that was a rookie mistake 🤦 I'm quite certain I finally understood the root cause. Thanks for pointing me in the right direction, otherwise I would have missed it again. The whole point of
if hasattr(wrapped_method, 'kwargs'):
wrapped_method.kwargs = dict(wrapped_method.kwargs)
|
When creating a copy of a method from a parent class we now: - Move application of the decorator to the last moment. - Shallow copy the existing schema extensions before applying decorator. This fixes tfranzel#218 where two child classes with @extend_schema_view affect each other - schema extensions are applied to the parent such that the second child overwrites the changes applied to the first child. This also fixes my case where a child with @extend_schema_view clobbered the schema extensions of the parent which also used @extend_schema_view.
No problem.
Yup. I think I was just hedging my bets with the
Bingo. Everything is stored under Out of interest,
I've opted for
Seems to work fine for the test cases I added.
Done. |
d8bbb22
to
4aaaa81
Compare
👍
I would rather call it layering (inheritance). The Schema classes are layered on top of each other. That behavior is intended like this and it follow one simple known rule: inheritance. i wouldn't know how to come up with another mechanic that is easier to understand.
Yes, it actually needs to be called like this. I did not come up with this naming or mechanic. It is the native DRF behaviour and DRF uses the drf-spectacular/drf_spectacular/utils.py Line 383 in 6bc1d2e
Awesome! |
hey @ngnpope, so I did some more investigating and found 2 more isolation issues.
Had to refactor the isolation code as it is required at multiple places actually. Please test these changes if something is behaving in a weird way, though i'm quite sure this eliminated a lot of weird behavior. the amount of magic that is generated by |
Thanks @tfranzel, that's great. It looks good for me so far. |
When creating a copy of a method from a parent class we now:
__qualname__
is defined correctlyChild.method
instead ofParent.method
.This fixes #218 where two child classes with
@extend_schema_view
affect each other - schema extensions are applied to the parent such that the second child overwrites the changes applied to the first child.This also fixes my case where a child with
@extend_schema_view
clobbered the schema extensions of the parent which also used@extend_schema_view
.