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
Don't rely on the database to generate the schema #874
Comments
Hey @jooola so lets start from the top to get you oriented:
I can see that Regarding |
so I was curious and had another look at funkwhale, as it may serve as a good example on how spectacular can make complicated setups work. so the root of all this is Still unsure why So you either change the base class of that field (to go into the right parsing branch) or you write a field extensions to the effect of something like this: class FixCustomRelatedField(OpenApiSerializerFieldExtension):
target_class = 'funkwhale_api.common.serializers.RelatedField'
match_subclasses = False
def map_serializer_field(self, auto_schema, direction):
# something like the content of serializers.PrimaryRelatedField:
# https://github.com/tfranzel/drf-spectacular/blob/master/drf_spectacular/openapi.py#L662
return build_basic_type(OpenApiTypes.UUID) Sidenote 1: I saw your Sidenote 2: All in all the schema looks quite nice. I think with a couple of type hints ( |
what is this field supposed to do? a given serializer on response and an UUID on request? If that is the case then you would probably rather do something like this: class FixCustomRelatedField(OpenApiSerializerFieldExtension):
target_class = 'funkwhale_api.common.serializers.RelatedField'
match_subclasses = True
def map_serializer_field(self, auto_schema, direction):
if direction == 'request':
return build_basic_type(OpenApiTypes.UUID)
elif direction == 'response' and self.target.serializer:
component = auto_schema.resolve_serializer(self.target.serializer, direction)
return component.ref
else:
# happens for
# UserViewSet: UserWriteSerializer: not sure how this works for: avatar
# AlbumViewSet: AlbumCreateSerializer: not sure how this works for: artist
warn(f'not sure how this works for: {self.target.field_name}')
return build_basic_type(OpenApiTypes.UUID) |
Waouw @tfranzel Thanks a lot for you insight, didn't expect that much. I was not even sure if I should keep the issue open as it was a problem on our side. Yesterday after writing this issue, I had another look at the code, and we have something weird in The I have noted all your ideas and notes, I'll have to take some time to understand and try them out. |
@jooola when I took a look I drive-by fixed a few more things. you can use it if you like.
you likely can use the FAQ entry I mentioned above, if you cannot move the query outside of regarding I got it down to 18 warnings. Most of the remaining warnings should not be too difficult to fix. class CustomRelatedFieldExtension(OpenApiSerializerFieldExtension):
target_class = 'funkwhale_api.common.serializers.RelatedField'
match_subclasses = True
def map_serializer_field(self, auto_schema, direction):
if direction == 'request':
return build_basic_type(OpenApiTypes.UUID)
elif direction == 'response' and self.target.serializer:
component = auto_schema.resolve_serializer(self.target.serializer, direction)
return component.ref
else:
# happens for
# UserViewSet: UserWriteSerializer: not sure how this works for: avatar
# AlbumViewSet: AlbumCreateSerializer: not sure how this works for: artist
# warn(f'not sure how this works for: {self.target.field_name}')
return build_basic_type(OpenApiTypes.UUID)
class PreferenceSerializerExtension(OpenApiSerializerExtension):
target_class = 'dynamic_preferences.api.serializers.PreferenceSerializer'
match_subclasses = True
def map_serializer(self, auto_schema, direction):
from dynamic_preferences.api.serializers import PreferenceSerializer
class Fix(PreferenceSerializer):
def get_default(self, o) -> str:
pass
def get_verbose_name(self, o) -> str:
pass
def get_identifier(self, o) -> str:
pass
def get_help_text(self, o) -> str:
pass
def get_additional_data(self, o) -> dict:
pass
def get_field(self, o) -> dict:
pass
""" override for customized serializer mapping """
return auto_schema._map_serializer(Fix, direction, bypass_extensions=True)
class CustomOAuthScheme(DjangoOAuthToolkitScheme):
target_class = "funkwhale_api.common.authentication.OAuth2Authentication"
def get_security_requirement(self, auto_schema):
from funkwhale_api.users.oauth.permissions import ScopePermission
from funkwhale_api.users.oauth.permissions import METHOD_SCOPE_MAPPING
for permission in auto_schema.view.get_permissions():
if isinstance(permission, ScopePermission):
scope_config = getattr(auto_schema.view, "required_scope", "noopscope")
if isinstance(scope_config, str):
scope_config = {
"read": f"read:{scope_config}",
"write": f"write:{scope_config}",
}
action = METHOD_SCOPE_MAPPING[auto_schema.view.request.method.lower()]
required_scope = scope_config[action]
else:
# we have a dict with explicit viewset actions / scopes
required_scope = scope_config[auto_schema.view.action]
return {self.name: [required_scope]}
|
cc @georgkrause this might also be of interest to you |
closing this issue for now. feel free to comment if anything is missing or not working and we will follow-up. |
Describe the bug
To generate the schema for a project I am working on, the django project required a valid database connection. This makes generating the schema not easy.
I was always able to generate the schema without the database on other projects, and though this would be a bug. But I could be wrong and this is actually a requirement of DRF/spectacular ?
While there is probably a bug on our side, I though spectacular might want to handle this error, and not fail hard.
But I've done some digging, and maybe this can be fixed:
A trace when trying to generate the schema:
From this trace I was able to find:
https://github.com/tfranzel/drf-spectacular/blob/master/drf_spectacular/openapi.py#L870
And maybe also https://github.com/tfranzel/drf-spectacular/blob/master/drf_spectacular/openapi.py#L861
Which seemed to trigger a
smart_repr
for some fields, and somehow required the database (relation name ?).I changed the line with:
And now the schema generation completes, even if I still have a database connection error in the middle:
But this final error, I can still fix later on.
To Reproduce
I couldn't extract a snippet to reproduce this, but here is the project repository: https://dev.funkwhale.audio/funkwhale/funkwhale/
Here is the CI job that generates the schema, I would like to remove the database requirement.
https://dev.funkwhale.audio/funkwhale/funkwhale/-/blob/ad3674e29e51e80875b2568ab3b87f4739b992bd/.gitlab-ci.yml#L251-L278
Expected behavior
I would like to generate the schema without the any database connection required, and if a bug occur our side, spectacular should throw a usefull warning.
The text was updated successfully, but these errors were encountered: