Skip to content

Commit

Permalink
Release 5.11.3
Browse files Browse the repository at this point in the history
### Changelog:
* Feature(backend): Provide ``ignore_meta`` argument to ``BaseModel.get_view_class()``.

Closes vst/vst-utils#662+

See merge request vst/vst-utils!673
  • Loading branch information
flwd3m committed Oct 23, 2024
2 parents c5be460 + e88b25f commit a0d259b
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 31 deletions.
2 changes: 1 addition & 1 deletion doc/backend.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Serializers
~~~~~~~~~~~

.. automodule:: vstutils.api.serializers
:members: DisplayMode,DisplayModeList,BaseSerializer,VSTSerializer,EmptySerializer,JsonObjectSerializer
:members: DisplayMode,DisplayModeList,BaseSerializer,VSTSerializer,DetailsResponseSerializer,EmptySerializer,JsonObjectSerializer

Views
~~~~~
Expand Down
71 changes: 58 additions & 13 deletions doc/locale/ru/LC_MESSAGES/backend.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VST Utils 5.0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-25 05:35+0000\n"
"POT-Creation-Date: 2024-10-23 02:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -452,6 +452,19 @@ msgstr ""
"использует такие же атрибуты, которые были объявлены в мета-атрибутах, но"
" позволяет перегружать отдельные части."

#: of vstutils.models.BModel:196
msgid ""
"An important aspect that distinguishes ``get_view_class()`` is the "
"``ignore_meta`` argument. It takes ``True`` when it's necessary to "
"completely ignore the values set in the model's ``Meta`` class. This "
"allows ``get_view_class()`` to generate a fully independent view."
msgstr ""
"Важным аспектом, который отличает ``get_view_class()``, является аргумент"
" ``ignore_meta``. Он принимает значение ``True``, когда необходимо "
"полностью игнорировать значения, установленные в классе ``Meta`` модели. "
"Это позволяет ``get_view_class()`` создать полностью независимое "
"представление."

#: ../../docstring of vstutils.models.BModel.hidden:1
msgid "If hidden is set to True, entry will be excluded from query in BQuerySet."
msgstr ""
Expand Down Expand Up @@ -2900,6 +2913,38 @@ msgstr ""
"В этом примере класс ``MySerializer`` расширяет ``BaseSerializer`` и "
"включает дополнительное созданное поле."

#: of vstutils.api.serializers.DetailsResponseSerializer:1
msgid "Serializer class inheriting from :class:`.BaseSerializer`."
msgstr "Класс сериализатора, наследующийся от :class:`.BaseSerializer`."

#: of vstutils.api.serializers.DetailsResponseSerializer:3
msgid ""
"This serializer is primarily intended for use as the "
"``result_serializer_class`` argument in "
":class:`vstutils.api.actions.Action` and its derivatives. It defines a "
"single read-only ``details`` field, which is useful for returning detail "
"messages in API responses."
msgstr ""
"Этот сериализатор в первую очередь предназначен для использования в качестве "
"аргумента ``result_serializer_class`` в "
":class:`vstutils.api.actions.Action` и его производных. Он определяет одно "
"поле только для чтения ``details``, которое полезно для возврата сообщений "
"с деталями в ответах API."

#: of vstutils.api.serializers.DetailsResponseSerializer:7
msgid ""
"Additionally, it can serve as a placeholder for schemas involving various"
" errors, where the error text is placed in the ``details`` field (the "
"default behavior in Django REST Framework)."
msgstr ""
"Кроме того, он может служить в качестве шаблона для схем, связанных с "
"различными ошибками, где текст ошибки помещается в поле ``details`` (это "
"поведение по умолчанию в Django REST Framework)."

#: of vstutils.api.serializers.DetailsResponseSerializer:10
msgid "Example usage with an Action:"
msgstr "Пример использования с Action:"

#: of vstutils.api.serializers.DisplayMode:1
msgid ""
"Enumeration for specifying how a serializer should be displayed on the "
Expand Down Expand Up @@ -3832,7 +3877,7 @@ msgstr ""
"использованием :class:`.SimpleAction` или алгоритм значительно более "
"сложный, чем стандартные операции CRUD."

#: of vstutils.api.actions.Action:39
#: of vstutils.api.actions.Action:35
msgid ""
"Flag indicating which type of action is used: on a list or on a single "
"entity. Affects where this action will be displayed - on a detailed "
Expand All @@ -3842,27 +3887,27 @@ msgstr ""
"отдельной сущности. Влияет на то, где будет отображаться это действие - "
"на детальной записи или на списке записей."

#: of vstutils.api.actions.Action:42
#: of vstutils.api.actions.Action:38
msgid ""
"List of available HTTP-methods for this action. Default has only `POST` "
"method."
msgstr "Список доступных методов HTTP. По умолчанию ``[\"post\"]``."

#: of vstutils.api.actions.Action:44
#: of vstutils.api.actions.Action:40
msgid "Request body serializer. Also used for default response."
msgstr ""
"Сериализатор для тела запроса. Используется также для формирования ответа"
" по умолчанию."

#: of vstutils.api.actions.Action:46
#: of vstutils.api.actions.Action:42
msgid ""
"Response body serializer. Required, when request and response has "
"different set of fields."
msgstr ""
"Сериализатор для тела ответа. Необходим, когда запрос и ответ имеют "
"различные наборы полей."

#: of vstutils.api.actions.Action:49
#: of vstutils.api.actions.Action:45
msgid ""
"GET-request query data serializer. It is used when it is necessary to get"
" valid data in the query data of a GET-request and cast it to the "
Expand All @@ -3872,35 +3917,35 @@ msgstr ""
"получить корректные данные в строке запроса типа GET и привести их к "
"нужному типу."

#: of vstutils.api.actions.Action:52
#: of vstutils.api.actions.Action:48
msgid ""
"Used only with non-GET requests and notify GUI, that this action should "
"be rendered over the selected list items."
msgstr ""
"Используется только с не-GET запросами и уведомляет GUI, что это действие"
" должно быть применено к выбранным элементам списка."

#: of vstutils.api.actions.Action:55
#: of vstutils.api.actions.Action:51
msgid ""
"Title for action in UI. For non-GET actions, title is generated from "
"method's name."
msgstr ""
"Заголовок действия в пользовательском интерфейсе. Для действий, отличных "
"от GET, заголовок генерируется на основе имени метода."

#: of vstutils.api.actions.Action:57
#: of vstutils.api.actions.Action:53
msgid "List of icons for UI button."
msgstr "Список иконок для кнопки пользовательского интерфейса."

#: of vstutils.api.actions.Action:59
#: of vstutils.api.actions.Action:55
msgid ""
"Flag indicating whether the action type is a list or a single entity. "
"Typically used with GET actions."
msgstr ""
"Флаг, указывающий, является ли тип действия списком или отдельной "
"сущностью. Обычно используется с действиями GET."

#: of vstutils.api.actions.Action:62
#: of vstutils.api.actions.Action:58
msgid ""
"Flag indicating whether the action will only use edit mode, without a "
"view page. This is used for actions where there is a GET method and any "
Expand All @@ -3911,13 +3956,13 @@ msgstr ""
"экшенов, где есть метод GET и любые другие изменяющие методы (POST, PUT, "
"PATCH)."

#: of vstutils.api.actions.Action:66
#: of vstutils.api.actions.Action:62
msgid "If true user will be asked to confirm action execution on frontend."
msgstr ""
"Если истина, то в интерфейсе пользователь должен будет подтвердить "
"действие перед выполнением."

#: of vstutils.api.actions.Action:68
#: of vstutils.api.actions.Action:64
msgid "Set of named arguments for :func:`rest_framework.decorators.action`."
msgstr "Набор именованных аргументов для :func:`rest_framework.decorators.action`."

Expand Down
14 changes: 13 additions & 1 deletion doc/locale/ru/LC_MESSAGES/quickstart.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VST Utils 5.0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-25 05:35+0000\n"
"POT-Creation-Date: 2024-10-23 02:07+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -830,6 +830,18 @@ msgstr ""
"использует такие же атрибуты, которые были объявлены в мета-атрибутах, но"
" позволяет перегружать отдельные части."

#: of vstutils.models.BModel:196
msgid ""
"An important aspect that distinguishes ``get_view_class()`` is the "
"``ignore_meta`` argument. It takes ``True`` when it's necessary to "
"completely ignore the values set in the model's ``Meta`` class. This "
"allows ``get_view_class()`` to generate a fully independent view."
msgstr ""
"Важным аспектом, который отличает ``get_view_class()``, является аргумент "
"``ignore_meta``. Он принимает значение ``True``, когда необходимо полностью "
"игнорировать значения, установленные в классе ``Meta`` модели. Это позволяет "
"``get_view_class()`` создать полностью независимое представление."

#: ../../quickstart.rst:206
msgid ""
"More information about Models you can find in `Django Models "
Expand Down
3 changes: 3 additions & 0 deletions test_src/test_proj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
API[VST_API_VERSION][r'cacheable'] = dict(
view='test_proj.views.CacheableViewSet'
)
API[VST_API_VERSION][r'cacheable_check_generation'] = dict(
view='test_proj.views.CacheableViewCheckGeneration'
)
API[VST_API_VERSION][r'dynamic_fields'] = dict(
model='test_proj.models.dynamic_fields.DynamicFields'
)
Expand Down
4 changes: 4 additions & 0 deletions test_src/test_proj/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4150,6 +4150,10 @@ def test_swagger_schema(self):
self.assertIn('Etag', data['paths']['/cacheable/{id}/']['put']['responses']['200']['headers'])
self.assertIn('412', data['paths']['/cacheable/{id}/']['put']['responses'])

# check that generated view ignore values from CachableProxyModel.Meta
self.assertCount(data['paths']['/cacheable_check_generation/'].keys(), 2, data['paths']['/cacheable_check_generation/'].keys()) # get and parameters
self.assertFalse('/cacheable_check_generation/{id}/' in data['paths']) # list only view

self.assertEqual(
data['paths']['/testbinaryfiles/{id}/test_some_filefield_default/']['get']['responses']['200']['schema']['type'],
'file'
Expand Down
5 changes: 5 additions & 0 deletions test_src/test_proj/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ def get_etag_value(self, model_class, request):
return super().get_etag_value((model_class, gui_version), request)


CacheableViewCheckGeneration = CachableProxyModel.get_view_class(
ignore_meta=True,
view_class=('list_only',),
)


class TestOauth2ViewSet(GenericViewSet):
serializer_class = EmptySerializer
Expand Down
2 changes: 1 addition & 1 deletion vstutils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# pylint: disable=django-not-available
__version__: str = '5.11.2'
__version__: str = '5.11.3'
8 changes: 2 additions & 6 deletions vstutils/api/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Action:
...
from vstutils.api.fields import VSTCharField
from vstutils.api.serializers import BaseSerializer
from vstutils.api.serializers import BaseSerializer, DetailsResponseSerializer
from vstutils.api.base import ModelViewSet
from vstutils.api.actions import Action
...
Expand All @@ -50,15 +50,11 @@ class RequestSerializer(BaseSerializer):
...
class ResponseSerializer(BaseSerializer):
detail = VSTCharField(read_only=True)
class AuthorViewSet(ModelViewSet):
model = ...
...
@Action(serializer_class=RequestSerializer, result_serializer_class=ResponseSerializer, ...)
@Action(serializer_class=RequestSerializer, result_serializer_class=DetailsResponseSerializer, ...)
def profile(self, request, *args, **kwargs):
''' Got `serializer_class` body and response with `result_serializer_class`. '''
serializer = self.get_serializer(self.get_object(), data=request.data)
Expand Down
41 changes: 34 additions & 7 deletions vstutils/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,39 @@ def build_relational_field(self, field_name, relation_info):
return super().build_relational_field(field_name, relation_info)


class DetailsResponseSerializer(BaseSerializer):
"""
Serializer class inheriting from :class:`.BaseSerializer`.
This serializer is primarily intended for use as the ``result_serializer_class`` argument in
:class:`vstutils.api.actions.Action` and its derivatives. It defines a single read-only
``details`` field, which is useful for returning detail messages in API responses.
Additionally, it can serve as a placeholder for schemas involving various errors, where the
error text is placed in the ``details`` field (the default behavior in Django REST Framework).
Example usage with an Action:
.. sourcecode:: python
class AuthorViewSet(ModelViewSet):
model = ...
...
@Action(
serializer_class=RequestSerializer,
result_serializer_class=DetailsResponseSerializer, # used
...
)
def profile(self, request, *args, **kwargs):
'''Process the request data and respond with a detail message.'''
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
return {"details": "Executed!"}
"""
details = drf_fields.CharField(read_only=True, allow_blank=True)


class EmptySerializer(BaseSerializer):
"""
Default serializer for empty responses.
Expand Down Expand Up @@ -320,15 +353,9 @@ class JsonObjectSerializer(DataSerializer):
pass


class ErrorSerializer(DataSerializer):
class ErrorSerializer(DetailsResponseSerializer):
detail = fields.VSTCharField(required=True)

def to_internal_value(self, data):
return data

def to_representation(self, instance):
return instance


class ValidationErrorSerializer(ErrorSerializer):
detail = serializers.DictField(required=True) # type: ignore
Expand Down
4 changes: 4 additions & 0 deletions vstutils/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ class or string to import with base class ViewSet.
Developers can use this method to customize various aspects of the associated view, such as serializer classes,
field configurations, filter backends, permission classes, etc. It uses attributes declared in meta attributes,
but allows individual parts to be overriden.
An important aspect that distinguishes ``get_view_class()`` is the ``ignore_meta`` argument.
It takes ``True`` when it's necessary to completely ignore the values set in the model's ``Meta`` class.
This allows ``get_view_class()`` to generate a fully independent view.
"""

#: Primary field for select and search in API.
Expand Down
8 changes: 6 additions & 2 deletions vstutils/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,12 @@ def get_view_class(cls, **extra_options): # noqa: CFQ001,R0914
Method which return autogenerated ViewSet based on model's Meta class.
"""
# pylint: disable=no-value-for-parameter
metadata = cls.get_extra_metadata()
metadata.update(extra_options)
if not extra_options.get('ignore_meta', False):
metadata = cls.get_extra_metadata()
metadata.update(extra_options)
else:
metadata = {**default_extra_metadata, **extra_options}

list_fields = _ensure_pk_in_fields(cls, metadata['list_fields'])
detail_fields = _ensure_pk_in_fields(cls, metadata['detail_fields'] or list_fields)

Expand Down
3 changes: 3 additions & 0 deletions vstutils/models/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class NestedViewOptionArgs(TypedDict, total=False):
NestedOptionType = Dict[Text, Union[NestedModelOptionArgs, NestedViewOptionArgs, dict]]

class ExtraMetadata(TypedDict, total=False):
ignore_meta: Optional[bool]
view_class: Optional[Union[Tuple, List[Union[Text, ConstantViewType]], Text, ConstantViewType]]
serializer_class: Optional[Serializer]
serializer_class_name: Optional[Text]
Expand Down Expand Up @@ -93,6 +94,8 @@ class ModelBaseClass(ModelBase):

def get_view_class(
cls,
*,
ignore_meta: Optional[bool],
view_class: Optional[Union[Tuple, List[Union[Text, ConstantViewType]], Text, ConstantViewType]] = None,

serializer_class: Optional[Serializer] = None,
Expand Down

0 comments on commit a0d259b

Please sign in to comment.