Backend API manual
VST Utils framework uses Django, Django Rest Framework, drf-yasg and Celery.
Models
A model is the single, definitive source of truth about your data. It contains essential fields and behavior for the data you’re storing. Usually best practice is to avoid writing views and serializers manually, as BModel provides plenty of Meta attributes to autogenerate serializers and views for many use cases.
Default Django model classes overrides in vstutils.models module.
- class vstutils.models.BModel(*args, **kwargs)[source]
Default model class that generates model viewset, separate serializers for list() and retrieve(), filters, api endpoints and nested views.
- Examples:
from django.db import models from rest_framework.fields import ChoiceField from vstutils.models import BModel class Stage(BModel): name = models.CharField(max_length=256) order = models.IntegerField(default=0) class Meta: default_related_name = "stage" ordering = ('order', 'id',) # fields which would be showed on list. _list_fields = [ 'id', 'name', ] # fields which would be showed on detail view and creation. _detail_fields = [ 'id', 'name', 'order' ] # make order as choices from 0 to 9 _override_detail_fields = { 'order': ChoiceField([str(i) for i in range(10)]) } class Task(BModel): name = models.CharField(max_length=256) stages = models.ManyToManyField(Stage) _translate_model = 'Task' class Meta: # fields which would be showed. _list_fields = [ 'id', 'name', ] # create nested views from models _nested = { 'stage': { 'allow_append': False, 'model': Stage } }
In this case, you create models which could converted to simple view, where:
POST
/GET
to/api/version/task/
- creates new or get list of tasksPUT
/PATCH
/GET
/DELETE
to/api/version/task/:id/
- updates, retrieves or removes instance of taskPOST
/GET
to/api/version/task/:id/stage/
- creates new or get list of stages in taskPUT
/PATCH
/GET
/DELETE
to/api/version/task/:id/stage/:stage_id
- updates, retrieves or removes instance of stage in task.
To attach a view to an API insert the following code in settings.py:
API[VST_API_VERSION][r'task'] = { 'model': 'your_application.models.Task' }
For primary access to generated view inherit from Task.generated_view property.
To make translation on frontend easier use
_translate_model
attribute with model_name.List of meta-attributes for generating a view:
_view_class
- list of additional view classes to inherit from, class or string to import with base class ViewSet. Constants are also supported:read_only
- to create a view only for viewing;list_only
- to create a view with list only;history
- to create a view only for viewing and deleting records.
CRUD-view is applied by default.
_serializer_class
- class of API serializer; use this attribute to specify parent class for autogenerated serializers. Default isvstutils.api.serializers.VSTSerializer
. Can take a string to import, serializer class ordjango.utils.functional.SimpleLazyObject
._serializer_class_name
- model name for OpenAPI definitions.This would be a model name in generated admin interface. Default is name of model class._list_fields
or_detail_fields
- list of fields which will be listed in entity list or detail view accordingly. Same as DRF serializers meta-attribute “fields”._override_list_fields
or_override_detail_fields
- mapping with names and field types that will be redeclared in serializer attributes(think of it as declaring fields in DRF ModelSerializer). Keep in mind that the fields specified here do not necessarily end up in the serializer class, but only if they are included in the corresponding lists._properties_groups
- dict with key as group name and value as list of fields(str). Allows to group fields in sections on frontend._view_field_name
- name of field frontend shows as main view name._non_bulk_methods
- list of methods which must not used via bulk requests._extra_serializer_classes
- mapping with additional serializers in viewset. For example, custom serializer, which will compute smth in action (mapping name). Value can be string for import. Important note: setting model attribute to None allows to use standard serializer generation mechanism and get fields from a list or detail serializer (set __inject_from__ serializer’s meta attribute to list or detail accordingly). In some cases, it is required to pass the model to the serializer. For these purposes, the constant LAZY_MODEL can be used as a meta attribute. Each time the serializer is used, the exact model where this serializer was declared will be set._filterset_fields
- list/dict of filterset names for API-filtering. Default is list of fields in list view. During processing a list of fields checks for the presence of special field names and inherit additional parent classes. If the list containsid
, class will inherit fromvstutils.api.filters.DefaultIDFilter
. If the list containsname
, class will inherit fromvstutils.api.filters.DefaultNameFilter
. If both conditions are present, inheritance will be from all of the above classes. Possible values include list of fields to filter or dict where key is a field name and value is a Filter class. Dict extends attribute functionality and provides ability to override filter field class (None value disables overriding)._search_fields
- tuple or list of fields using for search requests. By default (or None) get all filterable fields in detail view._copy_attrs
- list of model-instance attributes indicates that object is copyable with this attrs._nested
- key-value mapping with nested views (key - nested name, kwargs forvstutils.api.decorators.nested_view
decorator but supportsmodel
attribute as nested).model
can be string for import. Useoverride_params
when you need to override generated view parameters for nested view (works only withmodel
as view)._extra_view_attributes
- key-value mapping with additional view attributes, but has less priority over generated attributes._hidden_on_frontend_list_fields
- list with fields names which will be hidden in list view on frontend._hidden_on_frontend_detail_fields
- list with fields names which will be hidden in detail view on frontend._detail_operations_availability_field_name
- name of field which indicates availability of operations, field value can be dict where key is operation name and value is boolean flag or simply list of available operations.
In common, you can also add custom attributes to override or extend the default list of processing classes. Supported view attributes are
filter_backends
,permission_classes
,authentication_classes
,throttle_classes
,renderer_classes
andparser_classes
. List of meta-attributes for settings of view is looks like:_pre_{attribute}
- List of classes included before defaults._{attribute}
- List of classes included after defaults._override_{attribute}
- boolean flag indicates that attribute override default viewset (otherwise appends). Default isFalse
.
Note
You may need to create an action on generated view. Use
vstutils.models.decorators.register_view_action
decorator with thedetail
argument to determine applicability to a list or detail entry. In this case, the decorated method will take an instance of the view object asself
attribute.Note
In some cases, inheriting models may require to inherit Meta class from the base model. If the Meta is explicitly declared in the base class, then you can get it through the attribute OriginalMeta and use it for inheritance.
Note
Docstring of model will be reused for view descriptions. It is possible to write both a general description for all actions and description for each action using the following syntax:
General description for all actions. action_name: Description for this action. another_action: Description for another action.
The
get_view_class()
method is a utility method in the Django ORM model designed to facilitate the configuration and instantiation of Django Rest Framework (DRF) Generic ViewSets. It allows developers to define and customize various aspects of the associated DRF view class. The arguments passed to the function fully correspond to those specified earlier for theMeta
class of the model, but without the underscore prefix.- Examples:
# Create simple list view with same fields TaskViewSet = Task.get_view_class(view_class='list_only') # Create view with overriding nested view params from rest_framework.mixins import CreateModelMixin TaskViewSet = Task.get_view_class( nested={ "milestones": { "model": Stage, "override_params": { "view_class": ("history", CreateModelMixin) }, }, }, )
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 theignore_meta
argument. It takesTrue
when it’s necessary to completely ignore the values set in the model’sMeta
class. This allowsget_view_class()
to generate a fully independent view.If hidden is set to True, entry will be excluded from query in BQuerySet.
- id
Primary field for select and search in API.
- class vstutils.models.Manager(*args, **kwargs)[source]
Default VSTUtils manager. Used by BaseModel and BModel. Uses BQuerySet as base.
- class vstutils.models.queryset.BQuerySet(model=None, query=None, using=None, hints=None)[source]
Represent a lazy database lookup for a set of objects. Allows to override default iterable class by custom_iterable_class attribute (class with __iter__ method which returns generator of model objects) and default query class by custom_query_class attribute (class inherited from
django.db.models.sql.query.Query
).- cleared()[source]
Filter queryset for models with attribute ‘hidden’ and exclude all hidden objects.
- get_paginator(*args, **kwargs)[source]
Returns initialized object of
vstutils.utils.Paginator
over current instance’s QuerySet. All args and kwargs go to to Paginator’s constructor.
- class vstutils.models.decorators.register_view_action(*args, **kwargs)[source]
Decorator for turning model methods to generated view actions. When a method is decorated, it becomes a part of the generated view and the self reference within the method points to the view object. This allows you to extend the functionality of generated views with custom actions.
The register_view_action decorator supports various arguments, and you can refer to the documentation for
vstutils.api.decorators.subaction()
to explore the complete list of supported arguments. These arguments provide flexibility in defining the behavior and characteristics of the generated view actions.Note
In scenarios where you’re working with proxy models that share a common set of actions, you can use the inherit named argument with a value of True. This allows the proxy model to inherit actions defined in the base model, reducing redundancy and promoting code reuse.
Note
In many cases, an action may not require any parameters and can be executed by sending an empty query. To streamline development and enhance efficiency, the register_view_action decorator sets the default serializer to
vstutils.api.serializers.EmptySerializer
. This means that the action expects no input data, making it convenient for actions that operate without additional parameters.Example:
This example demonstrates how to use the decorator to create a custom action within a model view. The
empty_action
method becomes part of the generated view and expects no input parameters.from vstutils.models import BModel from vstutils.models.decorators import register_view_action from vstutils.api.responses import HTTP_200_OK class MyModel(BModel): # ... model fields ... @register_view_action(detail=False, inherit=True) def empty_action(self, request, *args, **kwargs): # in this case `self` will be reference within the method points to the view object return HTTP_200_OK('OK')
Vstutils supports models that don’t necessitate direct database interaction or aren’t inherently tied to database tables. These models exhibit diverse behaviors, such as fetching data directly from class attributes, loading data from files, or implementing custom data retrieval mechanisms. Remarkably, there are models that, in a sense, implement the mechanism of SQL views with pre-defined queries. This flexibility allows developers to define a wide range of models that cater to specific data needs, from in-memory models to those seamlessly integrating external data sources. Vstutils’ model system is not confined to traditional database-backed structures, providing a versatile foundation for crafting various data representations.
- class vstutils.models.custom_model.ExternalCustomModel(*args, **kwargs)[source]
Represents a custom model designed for the self-implementation of requests to external services.
This model facilitates the seamless interaction with external services by allowing the passing of filtering, limiting, and sorting parameters to an external request. It is designed to receive data that is already filtered and limited.
To utilize this model effectively, developers need to implement the
get_data_generator()
class method. This method receives a query object containing the necessary parameters, enabling developers to customize interactions with external services.Example:
class MyExternalModel(ExternalCustomModel): # ... model fields ... class Meta: managed = False @classmethod def get_data_generator(cls, query): data = ... # some fetched data from the external resource or generated from memory calculations. for row in data: yield row
- classmethod get_data_generator(query)[source]
This class method must be implemented by derived classes to define custom logic for fetching data from an external service based on the provided query parameters.
Query object might contain the following parameters:
filter (dict): A dictionary specifying the filtering criteria.
exclude (dict): A dictionary specifying the exclusion criteria.
order_by (list): A list specifying the sorting order.
low_mark (int): The low index for slicing (if sliced).
high_mark (int): The high index for slicing (if sliced).
is_sliced (bool): A boolean indicating whether the query is sliced.
- Parameters:
query (dict) – An object containing filtering, limiting, and sorting parameters.
- Returns:
A generator that yields the requested data.
- Return type:
Generator
- Raises:
NotImplementedError – If the method is not implemented by the derived class.
- class vstutils.models.custom_model.FileModel(*args, **kwargs)[source]
Custom model that loads data from a YAML file instead of a database. The path to the file is specified in the FileModel.file_path attribute.
- Examples:
Suppose the source file is stored at /etc/authors.yaml with the following content:
- name: "Sergey Klyuykov" - name: "Michael Taran"
You can create a custom model using this file:
from vstutils.custom_model import FileModel, CharField class Authors(FileModel): name = CharField(max_length=512) file_path = '/etc/authors.yaml'
- class vstutils.models.custom_model.ListModel(*args, **kwargs)[source]
Custom model which uses a list of dicts with data (attribute ListModel.data) instead of database records. Useful when you have a simple list of data.
- Examples:
from vstutils.custom_model import ListModel, CharField class Authors(ListModel): name = CharField(max_length=512) data = [ {"name": "Sergey Klyuykov"}, {"name": "Michael Taran"}, ]
Sometimes, it may be necessary to switch the data source. For these purposes, you should use the setup_custom_queryset_kwargs function, which takes various named arguments, which are also passed to the data initialization function. One such argument for
ListModel
is date_source, which takes any iterable object.- Examples:
from vstutils.custom_model import ListModel, CharField class Authors(ListModel): name = CharField(max_length=512) qs = Authors.objects.setup_custom_queryset_kwargs(data_source=[ {"name": "Sergey Klyuykov"}, {"name": "Michael Taran"}, ])
In this case, we setup source list via setup_custom_queryset_kwargs function, and any other chained call is going to work with this data.
- Variables:
data (list) – List with data dicts. Empty by default.
- class vstutils.models.custom_model.ViewCustomModel(*args, **kwargs)[source]
Implements the SQL View programming mechanism over other models.
This model provides a mechanism for implementing SQL View-like behavior over other models. In the
get_view_queryset()
method, a base query is prepared, and all further actions are implemented on top of it.Example Usage:
class MyViewModel(ViewCustomModel): # ... model fields ... class Meta: managed = False @classmethod def get_view_queryset(cls): return SomeModel.objects.annotate(...) # add some additional annotations to query
- classmethod get_view_queryset()[source]
This class method must be implemented by derived classes to define custom logic for generating the base queryset for the SQL View.
- Returns:
The base queryset for the SQL View.
- Return type:
- Raises:
NotImplementedError – If the method is not implemented by the derived class.
Model Fields
- class vstutils.models.fields.FkModelField(to, on_delete, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, to_field=None, db_constraint=True, **kwargs)[source]
Extends
django.db.models.ForeignKey
. Use this field invstutils.models.BModel
to get vstutils.api.FkModelField in serializer. To set Foreign Key relation set to argument to string path to model or to Model Class as indjango.db.models.ForeignKey
- class vstutils.models.fields.HTMLField(*args, db_collation=None, **kwargs)[source]
Extends
django.db.models.TextField
. A simple field for storing HTML markup. The field is based ondjango.db.models.TextField
, therefore it does not support indexing and is not recommended for use in filters.
- class vstutils.models.fields.MultipleFieldFile(instance, field, name)[source]
Subclasses
django.db.models.fields.files.FieldFile
. ProvidesMultipleFieldFile.save()
andMultipleFieldFile.delete()
to manipulate the underlying file, as well as update the associated model instance.
- class vstutils.models.fields.MultipleFileDescriptor(field)[source]
Subclasses
django.db.models.fields.files.FileDescriptor
to handle list of files. Return a list ofMultipleFieldFile
when accessed so you can write code like:from myapp.models import MyModel instance = MyModel.objects.get(pk=1) instance.files[0].size
- class vstutils.models.fields.MultipleFileField(**kwargs)[source]
Subclasses
django.db.models.fields.files.FileField
. Field for storing a list of Storage-kept files. All args passed to FileField.- attr_class
alias of
MultipleFieldFile
- descriptor_class
alias of
MultipleFileDescriptor
- class vstutils.models.fields.MultipleFileMixin(**kwargs)[source]
Mixin suited to use with
django.db.models.fields.files.FieldFile
to transform it to a Field with list of files.
- class vstutils.models.fields.MultipleImageField(**kwargs)[source]
Field for storing a list of storage-kept images. All args are passed to
django.db.models.fields.files.ImageField
, except height_field and width_field, they are not currently implemented.- attr_class
alias of
MultipleImageFieldFile
- descriptor_class
alias of
MultipleFileDescriptor
- class vstutils.models.fields.MultipleImageFieldFile(instance, field, name)[source]
Subclasses
MultipleFieldFile
andImageFile mixin
, handles deleting _dimensions_cache when file is deleted.
- class vstutils.models.fields.MultipleNamedBinaryFileInJSONField(*args, db_collation=None, **kwargs)[source]
Extends
django.db.models.TextField
. Use this field invstutils.models.BModel
to get vstutils.api.MultipleNamedBinaryFileInJSONField in serializer.
- class vstutils.models.fields.MultipleNamedBinaryImageInJSONField(*args, db_collation=None, **kwargs)[source]
Extends
django.db.models.TextField
. Use this field invstutils.models.BModel
to get vstutils.api.MultipleNamedBinaryImageInJSONField in serializer.
- class vstutils.models.fields.NamedBinaryFileInJSONField(*args, db_collation=None, **kwargs)[source]
Extends
django.db.models.TextField
. Use this field invstutils.models.BModel
to get vstutils.api.NamedBinaryFileInJSONField in serializer.
- class vstutils.models.fields.NamedBinaryImageInJSONField(*args, db_collation=None, **kwargs)[source]
Extends
django.db.models.TextField
. Use this field invstutils.models.BModel
to get vstutils.api.NamedBinaryImageInJSONField in serializer.
- class vstutils.models.fields.WYSIWYGField(*args, db_collation=None, **kwargs)[source]
Extends
django.db.models.TextField
. A simple field for storing Markdown data. The field is based ondjango.db.models.TextField
, therefore it does not support indexing and is not recommended for use in filters.
Web API
Web API is based on Django Rest Framework with additional nested functions.
Fields
The Framework includes a list of convenient serializer fields. Some of them take effect only in generated admin interface.
Additional serializer fields for generating OpenAPI and GUI.
- class vstutils.api.fields.AutoCompletionField(*args, **kwargs)[source]
Serializer field that provides autocompletion on the frontend, using a specified list of objects.
- Parameters:
autocomplete (list, tuple, str) – Autocompletion reference. You can set a list or tuple with values or specify the name of an OpenAPI schema definition. For a definition name, the GUI will find the optimal link and display values based on the
autocomplete_property
andautocomplete_represent
arguments.autocomplete_property (str) – Specifies which attribute to retrieve from the OpenAPI schema definition model as the value. Default is ‘id’.
autocomplete_represent – Specifies which attribute to retrieve from the OpenAPI schema definition model as the representational value. Default is ‘name’.
use_prefetch (bool) – Prefetch values on the frontend in list view. Default is
True
.
Note
This functionality is effective only in the GUI. In the API, it behaves similarly to
VSTCharField
.- Usage:
This field is designed to be used in serializers where a user needs to input a value, and autocompletion based on a predefined list or an OpenAPI schema definition is desired. If an OpenAPI schema is specified, two additional parameters,
autocomplete_property
andautocomplete_represent
, can be configured to customize the appearance of the dropdown list.Example:
from vstutils.api import serializers from vstutils.api.fields import AutoCompletionField class MyModelSerializer(serializers.BaseSerializer): name = AutoCompletionField(autocomplete=['Option 1', 'Option 2', 'Option 3']) # or class MyModelSerializer(serializers.BaseSerializer): name = AutoCompletionField( autocomplete='MyModelSchema', autocomplete_property='custom_property', autocomplete_represent='display_name' )
- class vstutils.api.fields.Barcode128Field(*args, **kwargs)[source]
A field for representing data as a Barcode (Code 128) in the user interface.
This field accepts and validates data in the form of a valid ASCII string. It is designed to display the data as a Code 128 barcode in the graphical user interface. The underlying data is serialized or deserialized using the specified child field.
- Parameters:
child (rest_framework.fields.Field) – The original data field for serialization or deserialization. Default:
rest_framework.fields.CharField
- Example:
Suppose you have a model with a product_code field, and you want to display its Code 128 barcode representation in the GUI. You can use Barcode128Field in your serializer:
class Product(BModel): product_code = models.CharField(max_length=20) class ProductSerializer(VSTSerializer): barcode = Barcode128Field(child=serializers.CharField(source='product_code')) class Meta: model = Product fields = '__all__'
- class vstutils.api.fields.BinFileInStringField(*args, **kwargs)[source]
This field extends
FileInStringField
and is specifically designed to handle binary files. In the GUI, it functions as a file input field, accepting binary files from the user, which are then converted to base64-encoded strings and stored in this field.- Parameters:
media_types (tuple, list) – A list of MIME types to filter on the user’s side. Supports the use of
*
as a wildcard. Default: [‘*/*’]
Note
This functionality is effective only in the GUI. In the API, it behaves similarly to
VSTCharField
.
- class vstutils.api.fields.CSVFileField(*args, **kwargs)[source]
Field extends
FileInStringField
, using for works with csv files. This field provides the display of the loaded data in the form of a table.- Parameters:
items (Serializer) –
The config of the table. This is a drf or vst serializer which includes char fields which are the keys in the dictionaries into which the data from csv is serialized and the names for columns in a table. The fields must be in the order you want them to appear in the table. Following options may be included:
label
: human readable column namerequired
: Defines whether the field should be required. False by default.
min_column_width (int) – Minimum cell width. Default is 200 px.
delimiter (str) – The delimiting character.
lineterminator (str) – The newline sequence. Leave blank to auto-detect. Must be one of
\r
,\n
, or\r\n
.quotechar (str) – The character used to quote fields.
escapechar (str) – The character used to escape the quote character within a field.
media_types (tuple,list) – List of MIME types to select on the user’s side. Supported syntax using
*
. Default:['text/csv']
- class vstutils.api.fields.CheckboxBooleanField(*args, **kwargs)[source]
Boolean field that renders checkbox.
- class vstutils.api.fields.CommaMultiSelect(*args, **kwargs)[source]
Field that allows users to input multiple values, separated by a specified delimiter (default: “,”). It retrieves a list of values from another model or a custom list and provides autocompletion similar to
AutoCompletionField
. This field is suitable for property fields in a model where the main logic is already implemented or for use withmodel.CharField
.- Parameters:
select (str, tuple, list) – OpenAPI schema definition name or a list with values.
select_separator (str) – The separator for values. The default is a comma.
select_represent (select_property,) – These parameters function similarly to
autocomplete_property
andautocomplete_represent
. The default isname
.use_prefetch (bool) – Prefetch values on the frontend in list view. The default is
False
.make_link (bool) – Show values as links to the model. The default is
True
.dependence (dict) – A dictionary where keys are the names of fields from the same model, and values are the names of query filters. If at least one of the fields we depend on is non-nullable, required, and set to null, the autocompletion list will be empty, and the field will be disabled.
Example:
from vstutils.api import serializers from vstutils.api.fields import CommaMultiSelect class MyModelSerializer(serializers.BaseSerializer): tags = CommaMultiSelect( select="TagsReferenceSchema", select_property='slug', select_represent='slug', use_prefetch=True, make_link=False, dependence={'some_field': 'value'}, ) # or class MyModelSerializer(serializers.BaseSerializer): tags = CommaMultiSelect(select=['tag1', 'tag2', 'tag3'])
Note
This functionality is effective only in the GUI and works similarly to
VSTCharField
in the API.
- class vstutils.api.fields.CrontabField(*args, **kwargs)[source]
Simple crontab-like field which contains the schedule of cron entries to specify time. A crontab field has five fields for specifying day, date and time.
*
in the value field above means all legal values as in braces for that column.The value column can have a
*
or a list of elements separated by commas. An element is either a number in the ranges shown above or two numbers in the range separated by a hyphen (meaning an inclusive range).The time and date fields are:
field
allowed value
minute
0-59
hour
0-23
day of month
1-31
month
1-12
day of week
0-7 (0 or 7 is Sunday)
Default value of each field if not specified is
*
..---------------- minute (0 - 59) | .-------------- hour (0 - 23) | | .------------ day of month (1 - 31) | | | .---------- month (1 - 12) | | | | .-------- day of week (0 - 6) (Sunday=0 or 7) | | | | | * * * * *
- class vstutils.api.fields.DeepFkField(only_last_child=False, parent_field_name='parent', **kwargs)[source]
Extends
FkModelField
, specifically designed for hierarchical relationships in the frontend.This field is intended for handling ForeignKey relationships within a hierarchical or tree-like structure. It displays as a tree in the frontend, offering a clear visual representation of parent-child relationships.
Warning
This field intentionally does not support the
dependence
parameter, as it operates in a tree structure. Usage offilters
should be approached with caution, as inappropriate filters may disrupt the tree hierarchy.- Parameters:
only_last_child (bool) – If True, the field restricts the selection to nodes without children. Default is False. Useful when you want to enforce selection of leaf nodes.
parent_field_name (str) – The name of the parent field in the related model. Default is parent. Should be set to the ForeignKey field in the related model, representing the parent-child relationship. For example, if your related model has a ForeignKey like parent = models.ForeignKey(‘self’, …), then parent_field_name should be set to ‘parent’.
- Examples:
Consider a related model with a ForeignKey field representing parent-child relationships:
class Category(BModel): name = models.CharField(max_length=255) parent = models.ForeignKey('self', null=True, default=None, on_delete=models.CASCADE)
To use the DeepFkField with this related model in a serializer, you would set the parent_field_name to ‘parent’:
class MySerializer(VSTSerializer): category = DeepFkField(select=Category, parent_field_name='parent')
This example assumes a Category related model with a ForeignKey ‘parent’ field. The DeepFkField will then display the categories as a tree structure in the frontend, providing an intuitive selection mechanism for hierarchical relationships.
Note
Effective only in GUI. Works similarly to
rest_framework.fields.IntegerField
in API.
- class vstutils.api.fields.DependEnumField(*args, **kwargs)[source]
Field extends
DynamicJsonTypeField
but its value is not transformed to json and would be given as is. Useful forproperty
in models or for actions.- Parameters:
field (str) – field in model which value change will change type of current value.
types – key-value mapping where key is value of subscribed field and value is type (in OpenAPI format) of current field.
choices (dict) – variants of choices for different subscribed field values. Uses mapping where key is value of subscribed field and value is list with values to choice.
Note
Effective only in GUI. In API works similar to
VSTCharField
without value modification.
- class vstutils.api.fields.DependFromFkField(*args, **kwargs)[source]
A specialized field that extends
DynamicJsonTypeField
and validates field data based on afield_attribute
chosen in a related model. The field data is validated against the type defined by the chosen value offield_attribute
.Note
By default, any value of
field_attribute
validates asVSTCharField
. To override this behavior, set the class attribute{field_attribute}_fields_mapping
in the related model. The attribute should be a dictionary where keys are string representations of the values offield_attribute
, and values are instances ofrest_framework.Field
for validation. If a value is not found in the dictionary, the default type will beVSTCharField
.- Parameters:
field (str) – The field in the model whose value change determines the type of the current value. The field must be of type
FkModelField
.field_attribute (str) – The attribute of the related model instance containing the name of the type.
types (dict) – A key-value mapping where the key is the value of the subscribed field, and the value is the type (in OpenAPI format) of the current field.
Warning
The
field_attribute
in the related model must be of typerest_framework.fields.ChoicesField
to ensure proper functioning in the GUI; otherwise, the field will be displayed as simple text.- Example:
Suppose you have a model with a ForeignKey field related_model and a field type_attribute in RelatedModel that determines the type of data. You can use DependFromFkField to dynamically adapt the serialization based on the value of type_attribute:
class RelatedModel(BModel): # ... other fields ... type_attribute = models.CharField(max_length=20, choices=[('type1', 'Type 1'), ('type2', 'Type 2')]) type_attribute_fields_mapping = { 'type1': SomeSerializer(), 'type2': IntegerField(max_value=1337), } class MyModel(BModel): related_model = models.ForeignKey(RelatedModel, on_delete=models.CASCADE) class MySerializer(VSTSerializer): dynamic_field = DependFromFkField( field='related_model', field_attribute='type_attribute' ) class Meta: model = MyModel fields = '__all__'
- class vstutils.api.fields.DynamicJsonTypeField(*args, **kwargs)[source]
A versatile serializer field that dynamically adapts its type based on the value of another field in the model. It facilitates complex scenarios where the type of data to be serialized depends on the value of a related field.
- Parameters:
field (str) – The field in the model whose value change will dynamically determine the type of the current field.
types (dict) – A key-value mapping where the key is the value of the subscribed field, and the value is the type (in OpenAPI format) of the current field.
choices (dict) – Variants of choices for different subscribed field values. Uses a mapping where the key is the value of the subscribed field, and the value is a list with values to choose from.
source_view (str) – Allows using parent views data as a source for field creation. The exact view path (/user/{id}/) or relative parent specifier (<<parent>>.<<parent>>.<<parent>>) can be provided. For example, if the current page is /user/1/role/2/ and source_view is <<parent>>.<<parent>>, then data from /user/1/ will be used. Only detail views are supported.
- Example:
Suppose you have a serializer MySerializer with a field_type (e.g., a ChoiceField) and a corresponding object_data. The object_data field can have different types based on the value of field_type. Here’s an example configuration:
class MySerializer(VSTSerializer): field_type = serializers.ChoiceField(choices=['serializer', 'integer', 'boolean']) object_data = DynamicJsonTypeField( field='field_type', types={ 'serializer': SomeSerializer(), 'integer': IntegerField(max_value=1337), 'boolean': 'boolean', }, )
In this example, the object_data field dynamically adapts its type based on the selected value of field_type. The types argument defines different types for each possible value of field_type, allowing for flexible and dynamic serialization.
- class vstutils.api.fields.FileInStringField(*args, **kwargs)[source]
This field extends
VSTCharField
and stores the content of a file as a string.The value must be text (not binary) and is saved in the model as is.
- Parameters:
media_types (tuple, list) – A list of MIME types to filter on the user’s side. Supports the use of
*
as a wildcard. Default:['*/*']
Note
This setting only takes effect in the GUI. In the API, it behaves like
VSTCharField
.
- class vstutils.api.fields.FkField(*args, **kwargs)[source]
An implementation of ForeignKeyField, designed for use in serializers. This field allows you to specify which field of a related model will be stored in the field (default: “id”), and which field will represent the value on the frontend.
- Parameters:
select (str) – OpenAPI schema definition name.
autocomplete_property (str) – Specifies which attribute will be retrieved from the OpenAPI schema definition model as the value. Default is
id
.autocomplete_represent – Specifies which attribute will be retrieved from the OpenAPI schema definition model as the representational value. Default is
name
.field_type (type) – Defines the type of the autocomplete_property for further definition in the schema and casting to the type from the API. Default is passthrough but requires int or str objects.
use_prefetch (bool) – Prefetch values on the frontend at list-view. Default is
True
.make_link (bool) – Show the value as a link to the related model. Default is
True
.dependence (dict) –
A dictionary where keys are names of fields from the same model, and values are names of query filters. If at least one of the fields that we depend on is non-nullable, required, and set to null, the autocompletion list will be empty, and the field will be disabled.
There are some special keys for the dependence dictionary to get data that is stored on the frontend without additional database query:
'<<pk>>'
gets the primary key of the current instance,'<<view_name>>'
gets the view name from the Vue component,'<<parent_view_name>>'
gets the parent view name from the Vue component,'<<view_level>>'
gets the view level,'<<operation_id>>'
gets the operation_id,'<<parent_operation_id'>>
gets the parent_operation_id.
- Examples:
field = FkField(select=Category, dependence={'<<pk>>': 'my_filter'})
This filter will get the primary key of the current object and make a query on the frontend
/category?my_filter=3
where3
is the primary key of the current instance.- Parameters:
filters (dict) – A dictionary where keys are names of fields from a related model (specified by this FkField), and values are values of that field.
Note
The intersection of dependence.values() and filters.keys() will throw an error to prevent ambiguous filtering.
Note
Effective only in the GUI. Works similarly to
rest_framework.fields.IntegerField
in the API.
- class vstutils.api.fields.FkModelField(*args, **kwargs)[source]
Extends
FkField
, but stores referred model class. This field is useful fordjango.db.models.ForeignKey
fields in model to set.- Parameters:
select (vstutils.models.BModel,vstutils.api.serializers.VSTSerializer) – model class (based on
vstutils.models.BModel
) or serializer class which used in API and has path in OpenAPI schema.autocomplete_property (str) – this argument indicates which attribute will be get from OpenAPI schema definition model as value. Default is
id
.autocomplete_represent – this argument indicates which attribute will be get from OpenAPI schema definition model as represent value. Default is
name
.use_prefetch – prefetch values on frontend at list-view. Default is
True
.make_link – Show value as link to model. Default is
True
.
Warning
Model class get object from database during .to_internal_value execution. Be careful on mass save executions.
Warning
Permissions to model which is referred by this field, are not to be checked. You should check it manually in signals or validators.
- class vstutils.api.fields.HtmlField(*args, **kwargs)[source]
A specialized field for handling HTML text content, marked with the format:html.
Note
This field is designed for use in the graphical user interface (GUI) and functions similarly to
VSTCharField
in the API.- Example:
If you have a model with an html_content field that stores HTML-formatted text, you can use HtmlField in your serializer to handle this content in the GUI:
class MyModel(BModel): html_content = models.TextField() class MySerializer(VSTSerializer): formatted_html_content = HtmlField(source='html_content') class Meta: model = MyModel fields = '__all__'
- class vstutils.api.fields.MaskedField(*args, **kwargs)[source]
Extends the ‘rest_framework.serializers.CharField’ class. Field that applies a mask to the input value.
This field is designed for applying a mask to the input value on the frontend. It extends the ‘rest_framework.serializers.CharField’ and allows the use of the IMask library for defining masks.
- Parameters:
mask (dict, str) – The mask to be applied to the value. It can be either a dictionary or a string following the IMask library format.
- Example:
In a serializer, include this field to apply a mask to a value:
class MySerializer(serializers.Serializer): masked_value = MaskedField(mask='000-000')
This example assumes a serializer where the
masked_value
field represents a value with a predefined mask. TheMaskedField
will apply the specified mask on the frontend, providing a masked input for users.
Note
The effectiveness of this field is limited to the frontend, and the mask is applied during user input.
- class vstutils.api.fields.MultipleNamedBinaryFileInJsonField(*args, **kwargs)[source]
Extends
NamedBinaryFileInJsonField
but uses list of JSONs. Allows to operate with multiple files as list ofNamedBinaryFileInJsonField
.Attrs:
NamedBinaryInJsonField.file
: if True, accept only subclasses of File as input. If False, accept only string input. Default: False.- file_field
alias of
MultipleFieldFile
- class vstutils.api.fields.MultipleNamedBinaryImageInJsonField(*args, **kwargs)[source]
Extends
MultipleNamedBinaryFileInJsonField
but uses list of JSONs. Used for operating with multiple images and works as list ofNamedBinaryImageInJsonField
.- Parameters:
background_fill_color (str) – Color to fill area that is not covered by image after cropping. Transparent by default but will be black if image format is not supporting transparency. Can be any valid CSS color.
- class vstutils.api.fields.NamedBinaryFileInJsonField(*args, **kwargs)[source]
Field that represents a binary file in JSON format.
- Parameters:
file (bool) – If True, accept only subclasses of File as input. If False, accept only string input. Default: False.
post_handlers (tuple,list) – Functions to process the file after validation. Each function takes two arguments:
binary_data
(file bytes) andoriginal_data
(reference to the original JSON object). The function should return the processedbinary_data
.pre_handlers (tuple,list) – Functions to process the file before validation. Each function takes two arguments:
binary_data
(file bytes) andoriginal_data
(reference to the original JSON object). The function should return the processedbinary_data
.max_content_size (int) – Maximum allowed size of the file content in bytes.
min_content_size (int) – Minimum allowed size of the file content in bytes.
min_length (int) – Minimum length of the file name. Only applicable when
file=True
.max_length (int) – Maximum length of the file name. Only applicable when
file=True
.
This field is designed for storing binary files alongside their names in
django.db.models.CharField
ordjango.db.models.TextField
model fields. All manipulations involving decoding and encoding binary content data occur on the client, imposing reasonable limits on file size.Additionally, this field can construct a
django.core.files.uploadedfile.SimpleUploadedFile
from incoming JSON and store it as a file indjango.db.models.FileField
if the file attribute is set to True.Example:
In a serializer, include this field to handle binary files:
class MySerializer(serializers.ModelSerializer): binary_data = NamedBinaryFileInJsonField(file=True)
This example assumes a serializer where the
binary_data
field represents binary file information in JSON format. TheNamedBinaryFileInJsonField
will then handle the storage and retrieval of binary files in a user-friendly manner.The binary file is represented in JSON with the following properties:
name (str): Name of the file.
mediaType (str): MIME type of the file.
content (str or File): Content of the file. If file is True, it will be a reference to the file; otherwise, it will be base64-encoded content.
Warning
The client application will display the content as a download link. Users will interact with the binary file through the application, with the exchange between the Rest API and the client occurring through the presented JSON object.
- class vstutils.api.fields.NamedBinaryImageInJsonField(*args, **kwargs)[source]
Field that represents an image in JSON format, extending
NamedBinaryFileInJsonField
.- Parameters:
background_fill_color (str) – Color to fill the area not covered by the image after cropping. Transparent by default but will be black if the image format does not support transparency. Can be any valid CSS color.
This field is designed for handling image files alongside their names in
django.db.models.CharField
ordjango.db.models.TextField
model fields. It extends the capabilities ofNamedBinaryFileInJsonField
to specifically handle images.Additionally, this field validates the image using the following validators: -
vstutils.api.validators.ImageValidator
-vstutils.api.validators.ImageResolutionValidator
-vstutils.api.validators.ImageHeightValidator
When saving and with the added validators, the field will display a corresponding window for adjusting the image to the specified parameters, providing a user-friendly interface for managing images.
The image file is represented in JSON with the following properties:
name (str): Name of the image file.
mediaType (str): MIME type of the image file.
content (str or File): Content of the image file. If file is True, it will be a reference to the image file; otherwise, it will be base64-encoded content.
Warning
The client application will display the content as an image. Users will interact with the image through the application, with the exchange between the Rest API and the client occurring through the presented JSON object.
- class vstutils.api.fields.PasswordField(*args, **kwargs)[source]
Extends CharField but in schema set format to password. Show all characters as asterisks instead of real value in GUI.
- class vstutils.api.fields.PhoneField(*args, **kwargs)[source]
Extends the ‘rest_framework.serializers.CharField’ class. Field for representing a phone number in international format.
This field is designed for capturing and validating phone numbers in international format. It extends the ‘rest_framework.serializers.CharField’ and adds custom validation to ensure that the phone number contains only digits.
- Example:
In a serializer, include this field to handle phone numbers:
class MySerializer(VSTSerializer): phone_number = PhoneField()
This example assumes a serializer where the
phone_number
field represents a phone number in international format. ThePhoneField
will then handle the validation and representation of phone numbers, making it convenient for users to input standardized phone numbers.
The field will be displayed in the client application as an input field for entering a phone number, including the country code.
- class vstutils.api.fields.PlusMinusIntegerField(*args, **kwargs)[source]
Integer field that renders +/- buttons.
- class vstutils.api.fields.QrCodeField(*args, **kwargs)[source]
A versatile field for encoding various types of data into QR codes.
This field can encode a wide range of data into a QR code representation, making it useful for displaying QR codes in the user interface. It works by serializing or deserializing data using the specified child field.
- Parameters:
child (rest_framework.fields.Field) – The original data field for serialization or deserialization. Default:
rest_framework.fields.CharField
- Example:
Suppose you have a model with a data field, and you want to display its QR code representation in the GUI. You can use QrCodeField in your serializer:
class MyModel(BModel): data = models.CharField(max_length=255) class MySerializer(VSTSerializer): qr_code_data = QrCodeField(child=serializers.CharField(source='data')) class Meta: model = MyModel fields = '__all__'
In this example, the qr_code_data field will represent the QR code generated from the data field in the GUI. Users can interact with this QR code, and if their device supports it, they can scan the code for further actions.
- class vstutils.api.fields.RatingField(min_value=0, max_value=5, step=1, front_style='stars', **kwargs)[source]
Extends class ‘rest_framework.serializers.FloatField’. This field represents a rating form input on frontend. Grading limits can be specified with ‘min_value=’ and ‘max_value=’, defaults are 0 to 5. Minimal step between grades are specified in ‘step=’, default - 1. Frontend visual representation can be chosen with ‘front_style=’, available variants are listed in ‘self.valid_front_styles’.
For ‘slider’ front style, you can specify slider color, by passing valid color to ‘color=’. For ‘fa_icon’ front style, you can specify FontAwesome icon that would be used for displaying rating, by passing a valid FontAwesome icon code to ‘fa_class=’.
- class vstutils.api.fields.RedirectCharField(*args, **kwargs)[source]
Field for redirect by string. Often used in actions for redirect after execution.
Note
Effective only in GUI. Works similar to
rest_framework.fields.IntegerField
in API.
- class vstutils.api.fields.RedirectFieldMixin(**kwargs)[source]
Field mixin indicates that this field is used to send redirect address to frontend after some action.
- Parameters:
operation_name (str) – prefix for operation_id, for example if operation_id is history_get then operation_name is history
depend_field (str) – name of the field that we depend on, its value will be used for operation_id
concat_field_name (bool) – if True then name of the field will be added at the end of operation_id
- class vstutils.api.fields.RedirectIntegerField(*args, **kwargs)[source]
Field for redirect by id. Often used in actions for redirect after execution.
Note
Effective only in GUI. Works similar to
rest_framework.fields.IntegerField
in API.
- class vstutils.api.fields.RelatedListField(related_name, fields, view_type='list', serializer_class=None, **kwargs)[source]
Extends
VSTCharField
to represent a reverse ForeignKey relation as a list of related instances.This field allows you to output the reverse ForeignKey relation as a list of related instances. To use it, specify the
related_name
kwarg (related manager for reverse ForeignKey) and thefields
kwarg (list or tuple of fields from the related model to be included).By default,
VSTCharField
is used to serialize all field values and represent them on the frontend. You can specify the serializer_class and override fields as needed. For example, title, description, and other field properties can be set to customize frontend behavior.- Parameters:
related_name (str) – Name of a related manager for reverse ForeignKey.
fields (list[str], tuple[str]) – List of related model fields.
view_type (str) – Determines how fields are represented on the frontend. Must be either
list
ortable
.fields_custom_handlers_mapping (dict) – Custom handlers mapping, where key: field_name, value: callable_obj that takes params: instance[dict], fields_mapping[dict], model, field_name[str].
serializer_class (type) – Serializer to customize types of fields. If no serializer is provided,
VSTCharField
will be used for every field in the fields list.
Warning
This field is deprecated. Use serializers with the
many=True
attribute. To change the display on the page, usevstutils.api.serializers.DisplayModeList
.
- class vstutils.api.fields.RouterLinkField(*args, **kwargs)[source]
A read-only serializer field that displays a link to another page or a simple text label in the interface.
Expected Input:
This field expects a dictionary with the following keys:
link (optional): The URL or route to another page. If provided, the label will be displayed as a clickable link. If not provided, only the text label is shown. The value should be compatible with the Vue Router’s push method parameter. Ensure that the link points to an existing resource in the interface to avoid 404 errors.
label: The text to display. This is required whether or not a link is provided.
Note
For simpler use cases, you might consider using
FkField
.Examples:
Using a model class method:
from django.db.models import CharField from vstutils.api.fields import RouterLinkField from vstutils.models import BaseModel class Author(BaseModel): name = CharField() def get_link(self): return { 'link': f'/author/{self.id}/', 'label': f'Author: {self.name}', } AuthorViewSet = Author.get_view_class( detail_fields=['name', 'link'], override_detail_fields={ 'link': RouterLinkField(source='get_link'), }, )
In this example, the
get_link
method in theAuthor
model returns a dictionary containing thelink
andlabel
. TheRouterLinkField
uses this method to display the author’s name as a clickable link to their detail page.Using a custom field class:
from django.db.models import CharField from vstutils.api.fields import RouterLinkField from vstutils.models import BaseModel class Author(BaseModel): name = CharField() class AuthorLinkField(RouterLinkField): def to_representation(self, instance: Author): return super().to_representation({ 'link': f'/author/{instance.id}/', 'label': f'Author: {instance.name}', }) AuthorViewSet = Author.get_view_class( detail_fields=['name', 'link'], override_detail_fields={ 'link': AuthorLinkField(source='*'), }, )
In this example, we create a custom field
AuthorLinkField
by subclassingRouterLinkField
. We override theto_representation
method to return a dictionary with thelink
andlabel
for eachAuthor
instance. This custom field is then used in the viewset to display each author’s name as a clickable link.Tip
The field is read-only and is intended to display dynamic links based on the instance data.
If the
link
key is omitted orNone
, the field will display thelabel
as plain text instead of a link.
Warning
Always ensure that the
link
provided points to a valid route within your application to prevent users from encountering 404 errors.
- class vstutils.api.fields.SecretFileInString(*args, **kwargs)[source]
This field extends
FileInStringField
but hides its value in the admin interface.The value must be text (not binary) and is saved in the model as is.
- Parameters:
media_types (tuple, list) – A list of MIME types to filter on the user’s side. Supports the use of
*
as a wildcard. Default:['*/*']
Note
This setting only takes effect in the GUI. In the API, it behaves like
VSTCharField
.
- class vstutils.api.fields.TextareaField(*args, **kwargs)[source]
A specialized field that allows the input and display of multiline text.
Note
This field is designed for use in the graphical user interface (GUI) and functions similarly to
VSTCharField
in the API.- Example:
Suppose you have a model with a description field that can contain multiline text. You can use TextareaField in your serializer to enable users to input and view multiline text in the GUI:
class MyModel(BModel): description = models.TextField() class MySerializer(VSTSerializer): multiline_description = TextareaField(source='description') class Meta: model = MyModel fields = '__all__'
- class vstutils.api.fields.UptimeField(*args, **kwargs)[source]
Time duration field, in seconds, specifically designed for computing and displaying system uptime.
This field is effective only in the GUI and behaves similarly to
rest_framework.fields.IntegerField
in the API.The UptimeField class transforms time in seconds into a user-friendly representation on the frontend. It intelligently selects the most appropriate pattern from the following templates:
HH:mm:ss
(e.g., 23:59:59)dd HH:mm:ss
(e.g., 01d 00:00:00)mm dd HH:mm:ss
(e.g., 01m 30d 00:00:00)yy mm dd HH:mm:ss
(e.g., 99y 11m 30d 22:23:24)
Example:
class MySerializer(serializers.ModelSerializer): uptime = UptimeField()
This example assumes a serializer where the uptime field represents a time duration in seconds. The UptimeField will then display the duration in a human-readable format on the frontend, making it convenient for users to interpret and input values.
Note
Effective only in GUI. Works similarly to
rest_framework.fields.IntegerField
in API.
- class vstutils.api.fields.VSTCharField(*args, **kwargs)[source]
CharField (extends
rest_framework.fields.CharField
). This field translate any json type to string for model.
- class vstutils.api.fields.WYSIWYGField(*args, **kwargs)[source]
Extends the
TextareaField
class to render the https://ui.toast.com/tui-editor on the frontend.This field is specifically designed for rendering a WYSIWYG editor on the frontend, using the https://ui.toast.com/tui-editor. It saves data as markdown and escapes all HTML tags.
- Parameters:
escape (bool) – HTML-escape input. Enabled by default to prevent HTML injection vulnerabilities.
- Example:
In a serializer, include this field to render a WYSIWYG editor on the frontend:
class MySerializer(serializers.Serializer): wysiwyg_content = WYSIWYGField()
This example assumes a serializer where the
wysiwyg_content
field represents data to be rendered in a WYSIWYG editor on the frontend. TheWYSIWYGField
ensures that the content is saved as markdown and HTML tags are escaped to enhance security.
Warning
Enabling the
escape
option is recommended to prevent potential HTML injection vulnerabilities.Note
The rendering on the frontend is achieved using the https://ui.toast.com/tui-editor.
Validators
There are validation classes for fields.
- class vstutils.api.validators.FileMediaTypeValidator(extensions=None, **kwargs)[source]
Base Image Validation class. Validates media types.
- Parameters:
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
]) – Tuple or List of file extensions, that should pass the validation
Raises rest_framework.exceptions.ValidationError: in case file extension are not in the list
- class vstutils.api.validators.ImageBaseSizeValidator(extensions=None, **kwargs)[source]
Validates image size. To use this class for validating image width/height, rewrite self.orientation to (‘height’,) or (‘width’,) or (‘height’, ‘width’)
Raises rest_framework.exceptions.ValidationError: if not(min <= (height or width) <= max)
- Parameters:
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
])
- class vstutils.api.validators.ImageHeightValidator(extensions=None, **kwargs)[source]
Wrapper for ImageBaseSizeValidator that validates only height
- Parameters:
min_height – minimal height of an image being validated
max_height – maximal height of an image being validated
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
])
- class vstutils.api.validators.ImageOpenValidator(extensions=None, **kwargs)[source]
Image validator that checks if image can be unpacked from b64 to PIL Image obj. Won’t work if Pillow isn’t installed.
Raises rest_framework.exceptions.ValidationError if PIL throws error when trying to open image
- Parameters:
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
])
- class vstutils.api.validators.ImageResolutionValidator(extensions=None, **kwargs)[source]
Wrapper for ImageBaseSizeValidator that validates both height and width
- Parameters:
min_height – minimal height of an image being validated
max_height – maximal height of an image being validated
min_width – minimal width of an image being validated
max_width – maximal width of an image being validated
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
])
- class vstutils.api.validators.ImageValidator(extensions=None, **kwargs)[source]
Base Image Validation class. Validates image format. Won’t work if Pillow isn’t installed. Base Image Validation class. Validates media types.
- Parameters:
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
]) – Tuple or List of file extensions, that should pass the validation
Raises rest_framework.exceptions.ValidationError: in case file extension are not in the list
- property has_pillow
Check if Pillow is installed
- class vstutils.api.validators.ImageWidthValidator(extensions=None, **kwargs)[source]
Wrapper for ImageBaseSizeValidator that validates only width
- Parameters:
min_width – minimal width of an image being validated
max_width – maximal width of an image being validated
extensions (
typing.Union
[typing.Tuple
,typing.List
,None
])
- class vstutils.api.validators.RegularExpressionValidator(regexp=None)[source]
Class for regular expression based validation
- Raises:
rest_framework.exceptions.ValidationError – in case value does not match regular expression
- Parameters:
regexp (
typing.Optional
[typing.Pattern
])
- class vstutils.api.validators.UrlQueryStringValidator(regexp=None)[source]
Class for validation url query string, for example a=&b=1
- Parameters:
regexp (
typing.Optional
[typing.Pattern
])
- vstutils.api.validators.resize_image(img, width, height)[source]
Utility function to resize image proportional to specific values. Can create white margins if it’s needed to satisfy required size
- vstutils.api.validators.resize_image_from_to(img, limits)[source]
Utility function to resize image proportional to values between min and max values for each side. Can create white margins if it’s needed to satisfy restrictions
- Parameters:
img (PIL.Image) – Pillow Image object
limits (dict) – Dict with min/max side restrictions like:
{'width': {'min': 300, 'max: 600'}, 'height': {'min': 400, 'max: 800'}}
- Returns:
Pillow Image object
- Return type:
PIL.Image
Serializers
Default serializer classes for web-api. Read more in Django REST Framework documentation for Serializers.
- class vstutils.api.serializers.BaseSerializer(*args, **kwargs)[source]
Default serializer with logic to work with objects.
This serializer serves as a base class for creating serializers to work with non-model objects. It extends the ‘rest_framework.serializers.Serializer’ class and includes additional logic for handling object creation and updating.
Note
You can set the
generated_fields
attribute in theMeta
class to automatically include default CharField fields. You can also customize the field creation using thegenerated_field_factory
attribute.Example:
class MySerializer(BaseSerializer): class Meta: generated_fields = ['additional_field'] generated_field_factory = lambda f: drf_fields.IntegerField()
In this example, the
MySerializer
class extendsBaseSerializer
and includes an additional generated field.
- class vstutils.api.serializers.DetailsResponseSerializer(*args, **kwargs)[source]
Serializer class inheriting from
BaseSerializer
.This serializer is primarily intended for use as the
result_serializer_class
argument invstutils.api.actions.Action
and its derivatives. It defines a single read-onlydetails
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:
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!"}
- class vstutils.api.serializers.DisplayMode(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Enumeration for specifying how a serializer should be displayed on the frontend.
This class is used to set the
_display_mode
property in a serializer to control its UI behavior.Example:
To set the display mode to steps:
class MySerializer(serializers.Serializer): _display_mode = DisplayMode.STEP # Define serializer fields here
To use the default display mode:
class MySerializer(serializers.Serializer): _display_mode = DisplayMode.DEFAULT # Define serializer fields here
Using DisplayMode allows developers to customize the interface based on the workflow needs, making forms and data entry more user-friendly and intuitive.
- DEFAULT = 'DEFAULT'
Will be used if no mode provided.
- STEP = 'STEP'
Each properties group displayed as separate tab. On creation displayed as multiple steps.
- class vstutils.api.serializers.DisplayModeList(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Enumeration for specifying how a list serializer should be displayed on the frontend.
This class is used to set the
_display_mode_list
property in a list serializer to control its UI behavior when dealing with multiple instances.Example:
To set the list display mode to table view:
class MyRowSerializer(serializers.Serializer): _display_mode_list = DisplayModeList.TABLE # Define serializer fields here class MySerializer(serializers.Serializer): items = MyRowSerializer(many=True)
To use the default list display mode ensure that class doesn’t have
_display_mode_list
class property or set value toDisplayModeList.DEFAULT
.DisplayModeList enables developers to tailor the appearance of list serializers, ensuring that users can interact with multiple data entries effectively in the interface.
- DEFAULT = 'DEFAULT'
It will be displayed as a standard list of JSON objects.
- TABLE = 'TABLE'
It will be displayed as a table view.
- class vstutils.api.serializers.EmptySerializer(*args, **kwargs)[source]
Default serializer for empty responses. In generated GUI this means that action button won’t show additional view before execution.
- class vstutils.api.serializers.VSTSerializer(*args, **kwargs)[source]
Default model serializer based on
rest_framework.serializers.ModelSerializer
. Read more in DRF documentation how to create Model Serializers. This serializer matches model fields to extended set of serializer fields.List of available pairs specified in VSTSerializer.serializer_field_mapping. For example, to set
vstutils.api.fields.FkModelField
in serializer usevstutils.models.fields.FkModelField
in a model.Example:
class MyModel(models.Model): name = models.CharField(max_length=255) class MySerializer(VSTSerializer): class Meta: model = MyModel
In this example, the
MySerializer
class extendsVSTSerializer
and is associated with theMyModel
model.
Views
VSTUtils extends the standard behavior of ViewSets from Django REST Framework (DRF) by providing built-in mechanisms for managing model views that cater to the most commonly encountered development patterns. This framework enhancement simplifies the process of creating robust and scalable web applications by offering a rich set of tools and utilities that automate much of the boilerplate code required in API development. Through these extensions, developers can easily implement custom business logic, data validation, and access control with minimal effort, thus significantly reducing development time and improving code maintainability.
- class vstutils.api.base.CopyMixin(**kwargs)[source]
Mixin for viewsets which adds copy endpoint to view.
- copy_field_name = 'name'
Name of field which will get a prefix.
- copy_prefix = 'copy-'
Value of prefix which will be added to new instance name.
List of related names which will be copied to new instance.
- class vstutils.api.base.FileResponseRetrieveMixin(**kwargs)[source]
ViewSet mixin for retrieving FileResponse from models with file fields data.
Example:
import datetime import os import pydantic from django.db import models from django.db.models.functions import Now from rest_framework import permissions, fields as drf_fields from vstutils.api.serializers import BaseSerializer, DataSerializer from vstutils.models.decorators import register_view_action from vstutils.models.custom_model import ListModel, FileModel, ViewCustomModel from vstutils.api import fields, base, responses from .cacheable import CachableModel class TestQuerySerializer(BaseSerializer): test_value = drf_fields.ChoiceField(required=True, choices=("TEST1", "TEST2")) class FileViewMixin(base.FileResponseRetrieveMixin): # required always instance_field_data = 'value' # required for response caching in browser instance_field_timestamp = 'updated' # this is not required, but allow to understand file response mime type in schema produces_for_retrieve = ['application/octet-stream', 'application/pdf'] # search this field in instance for response filename # instance_field_filename = 'filename' # as default # headers for response caching (default works with user auth) # cache_control_header_data = 'private, no-cache' # # WARNING: # DO NOT OVERRIDE `serializer_class_retrieve` if you do not know what you do. class ListOfFiles(ListModel): data = [ {'id': i, 'value': 'File data', 'updated': datetime.datetime(2021, 3, 1, 16, 15, 51, 801564), 'test': 1} for i in range(1) ] id = models.IntegerField(primary_key=True) value = models.TextField() test = models.CharField(choices=tuple((i, i) for i in range(3)), max_length=3) updated = models.DateTimeField() class Meta: _view_class = (FileViewMixin, 'read_only') _list_fields = _detail_fields = ('id', 'filename', 'test',)
- serializer_class_retrieve
alias of
FileResponse
- class vstutils.api.base.GenericViewSet(**kwargs)[source]
The base class for all views. Extends the standard features of the DRF class. Here are some of the possibilities:
Provides
model
attribute instead ofqueryset
.Provides to set serializers for each action separately through a dictionary
action_serializers
or attributes starting withserializer_class_[action name]
.Provides to specify a serializer for lists and detail views separately.
Optimizes the database query for GET requests, if possible, by selecting only the fields necessary for the serializer.
- create_action_serializer(*args, **kwargs)[source]
A method that implements the standard logic for actions. It relies on the passed arguments to build logic. So, if the named argument data was passed, then the serializer will be validated and saved.
- Parameters:
autosave (bool) – Enables/disables the execution of saving by the serializer if named argument data passed. Enabled by default.
custom_data (dict) – Dict with data which will passed to validated_data without validation.
serializer_class (None,type[rest_framework.serializers.Serializer]) – Serializer class for this execution. May be useful when request and response serializers are different.
- Param:
data: Default serializer class argument with serializable data. Enables validation and saving.
- Param:
instance: Default serializer class argument with serializable instance.
- Returns:
Ready serializer with default logic performed.
- Return type:
rest_framework.serializers.Serializer
- get_query_serialized_data(request, query_serializer=None, raise_exception=True)[source]
Get request query data and serialize values if query_serializer_class attribute exists or attribute was send.
- Parameters:
request – DRF request object.
query_serializer – serializer class for processing parameters in query_params.
raise_exception – flag that indicates whether an exception should be thrown during validation in the serializer or not.
- get_serializer(*args, **kwargs)[source]
Return the serializer instance that should be used for validating and deserializing input, and for serializing output.
Provide to use
django.http.StreamingHttpResponse
as serializer init.
- class vstutils.api.base.HistoryModelViewSet(**kwargs)[source]
Default viewset like ReadOnlyModelViewSet but for historical data (allow to delete, but can’t create and update). Inherited from
GenericViewSet
.
- class vstutils.api.base.ModelViewSet(**kwargs)[source]
A viewset that provides CRUD actions under model. Inherited from
GenericViewSet
.- Variables:
model (vstutils.models.BModel) – DB model with data.
serializer_class (vstutils.api.serializers.VSTSerializer) – Serializer for view of Model data.
serializer_class_one (vstutils.api.serializers.VSTSerializer) – Serializer for view one instance of Model data.
serializer_class_[ACTION_NAME] (vstutils.api.serializers.VSTSerializer) – Serializer for view of any endpoint like .create.
- Examples:
from vstutils.api.base import ModelViewSet from . import serializers as sers class StageViewSet(ModelViewSet): # This is difference with DRF: # we use model instead of queryset model = sers.models.Stage # Serializer for list view (view for a list of Model instances serializer_class = sers.StageSerializer # Serializer for page view (view for one Model instance). # This property is not required, if its value is the same as `serializer_class`. serializer_class_one = sers.StageSerializer # Allowed to set decorator to custom endpoint like this: # serializer_class_create - for create method # serializer_class_copy - for detail endpoint `copy`. # etc...
- class vstutils.api.base.ReadOnlyModelViewSet(**kwargs)[source]
Default viewset like vstutils.api.base.ModelViewSet for readonly models. Inherited from
GenericViewSet
.
- class vstutils.api.decorators.nested_view(name, arg=None, methods=None, *args, **kwargs)[source]
By default, DRF does not support nested views. This decorator solves this problem.
You need two or more models with nested relationship (Many-to-Many or Many-to-One) and two viewsets. Decorator nests viewset to parent viewset class and generate paths in API.
- Parameters:
name (str) – Name of nested path. Also used as default name for related queryset (see manager_name).
arg (str) – Name of nested primary key field.
view (vstutils.api.base.ModelViewSet, vstutils.api.base.HistoryModelViewSet, vstutils.api.base.ReadOnlyModelViewSet) – Nested viewset class.
allow_append (bool) – Flag for allowing to append existed instances.
allow_bulk (bool) – Flag for forbidding bulk queries in related manager add method.
manager_name (str,Callable) – Name of model-object attr which contains nested queryset.
methods (list) – List of allowed methods to nested view endpoints.
subs (list,None) – List of allowed subviews or actions to nested view endpoints.
queryset_filters – List of callable objects which returns filtered queryset of main.
Note
Some view methods will not call for performance reasons. This also applies to some of the class attributes that are usually initialized in the methods. For example,
.initial()
will never call. Each viewset wrapped by nested class with additional logic.Example:
from vstutils.api.decorators import nested_view from vstutils.api.base import ModelViewSet from . import serializers as sers class StageViewSet(ModelViewSet): model = sers.models.Stage serializer_class = sers.StageSerializer nested_view('stages', 'id', view=StageViewSet) class TaskViewSet(ModelViewSet): model = sers.models.Task serializer_class = sers.TaskSerializer
This code generates api paths:
/tasks/ - GET, POST
/tasks/{id}/ - GET, PUT, PATCH, DELETE
/tasks/{id}/stages/ - GET, POST
/tasks/{id}/stages/{stages_id}/ - GET, PUT, PATCH, DELETE
- vstutils.api.decorators.subaction(*args, **kwargs)[source]
Decorator which wrap object method to subaction of viewset.
- Parameters:
methods – List of allowed HTTP-request methods. Default is
["post"]
.detail – Flag to set method execution to one instance.
serializer_class – Serializer for this action.
permission_classes – Tuple or list permission classes.
url_path – API-path name for this action.
description – Description for this action in OpenAPI.
multiaction – Allow to use this action in multiactions. Works only with
vstutils.api.serializers.EmptySerializer
as response.require_confirmation – Sets whether the action must be confirmed before being executed.
is_list – Mark this action as paginated list with all rules and parameters.
title – Override action title.
icons – Setup action icon classes.
An ETag (Entity Tag) is a mechanism defined by the HTTP protocol for web cache validation and to manage resource versions efficiently. It represents a unique identifier for the content of a resource at a given time, allowing client and server to determine if the resource has changed without downloading the entire content. This mechanism significantly reduces bandwidth and improves web performance by enabling conditional requests. Servers send ETags in responses to clients, which can cache these tags along with the resources. On subsequent requests, clients send the cached ETag back to the server in an If-None-Match header. If the resource has not changed (the ETag matches), the server responds with a 304 Not Modified status, indicating that the client’s cached version is up-to-date.
Beyond GET requests, ETags can also be instrumental in other HTTP methods like PUT or DELETE to ensure consistency and prevent unintended overwrites or deletions, known as “mid-air collision avoidance.” By including an ETag in the If-Match header of non-GET requests, clients signal that the operation should proceed only if the resource’s current state matches the specified ETag, thus safeguarding against concurrent modifications by different clients. This application of ETags enhances the reliability and integrity of web applications by ensuring that operations are performed on the correct version of a resource.
Here is main functionality provided for working with ETag’s mechanism:
- class vstutils.api.base.CachableHeadMixin(**kwargs)[source]
A mixin designed to enhance caching for GET responses in Django REST framework views, leveraging the standard HTTP caching mechanism. It returns a 304 Not Modified status for reading requests like GET or HEAD when the ETag (Entity Tag) in the client’s request matches the current resource state, and a 412 Precondition Failed status for writing requests when the condition fails. This approach reduces unnecessary network traffic and load times for unchanged resources, and ensures data consistency for write operations.
The mixin relies on
get_etag_value()
andcheck_request_etag()
functions within the GenericViewSet context to dynamically generate and validate ETags for resource states. By comparing ETags, it determines whether content has changed since the last request, allowing clients to reuse cached responses when applicable and preventing concurrent write operations from overwriting each other without acknowledgment of updated state.Warning
This mixin is designed to work with models that inherit from
vstutils.models.BModel
. Usage with other models may not provide the intended caching behavior and could lead to incorrect application behavior.Note
For effective use, ensure model classes are compatible with ETag generation and validation by implementing the
get_etag_value()
method for custom ETag computation. Additionally, theGenericViewSet
using this mixin should properly handle ETag headers in client requests to leverage HTTP caching.An additional feature of the
CachableHeadMixin
is its automatic inclusion in the generated view from avstutils.models.BModel
if the model class has the_cache_responses
class attribute set toTrue
. This enables automatic caching capabilities for models indicating readiness for HTTP-based caching, streamlining the process of optimizing response times and reducing server load for frequently accessed resources.
- class vstutils.api.base.EtagDependency(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
A custom enumeration that defines potential dependencies for ETag generation. It includes:
- LANG = 4
Indicates dependency on the user’s language preference.
- SESSION = 2
Indicates dependency on the user’s session.
- USER = 1
Indicates dependency on the user’s identity.
- vstutils.api.base.get_etag_value(view, model_class, request, pk=None)[source]
The get_etag_value function is designed to compute a unique ETag value based on the model’s state, request parameters, and additional dependencies such as user language, session, and user identity. This function supports both single models and collections of models.
- Parameters:
view (
GenericViewSet
) – An instance ofGenericViewSet
, responsible for view operations.model_class (
django.db.models.Model
,list
,tuple
, orset
) – The model class for which the ETag value is being generated. This parameter can be a single model class or a collection of model classes (list
,tuple
, orset
). Each model class may optionally implement a class method namedget_etag_value
.request (
rest_framework.request.Request
) – The request object from the Django REST framework, containing all the HTTP request information.pk – he primary key of the model instance for which the ETag is being calculated. This can be a specific value (int or str) for single model usage, or a dictionary mapping model classes to their respective primary key values for collections of models.
- Returns:
The computed ETag value as a hexadecimal string.
- Return type:
The function operates differently based on the type of model_class provided:
Collection of Models: When model_class is a collection, the function computes an ETag by concatenating ETag values of individual models in the collection, using a recursive call for each model. The ETag value for each model is encoded and hashed using Blake2s algorithm.
Model with ``get_etag_value`` method: If the model class has a get_etag_value method, the function calls this method to obtain a base ETag value. It then appends language, user ID, and session key information if they are marked as dependencies in the model’s
_cache_response_dependencies
attribute. This base ETag may be further processed to include the application’s full version string and hashed if user or session information is included.Model without ``get_etag_value`` method: For models lacking a custom get_etag_value method, the function generates an ETag based on the model’s class name and a hash of the application’s full version string.
- vstutils.utils.check_request_etag(request, etag_value, header_name='If-None-Match', operation_handler=<slot wrapper '__eq__' of 'str' objects>)[source]
The function plays a crucial role within the context of the ETag mechanism, providing a flexible way to validate client-side ETags against the server-side version for both cache validation and ensuring data consistency in web applications. It supports conditional handling of HTTP requests based on the match or mismatch of ETag values, accommodating various scenarios such as cache freshness checks and prevention of concurrent modifications.
- Parameters:
request (
rest_framework.request.Request
) – The HTTP request object containing the client’s headers, from which the ETag for comparison is retrieved.etag_value (
str
) – The server-generated ETag value that represents the current state of the resource. This unique identifier is recalculated whenever the resource’s content changes.header_name (
str
) – Specifies the HTTP header to look for the client’s ETag. Defaults to “If-None-Match”, commonly used in GET requests for cache validation. For operations requiring confirmation that the client is acting on the latest version of a resource (e.g., PUT or DELETE), “If-Match” should be used instead.operation_handler – A function to compare the ETags. By default, this is set to
str.__eq__
, which checks for an exact match between the client’s and server’s ETags, suitable for validating caches withIf-None-Match
. To handleIf-Match
scenarios, where the operation should proceed only if the ETags do not match, indicating the resource has been modified,str.__ne__
(not equal) can be used as the operation handler. This flexibility allows for precise control over how and when clients are allowed to read from or write to resources based on their version.
- Returns:
Returns a tuple containing the server’s ETag and a boolean flag. The flag is
True
if the operation handler condition between the server’s and client’s ETag is met, indicating the request should proceed based on the matching logic defined by the operation handler; otherwise, it returnsFalse
.
Actions
Vstutils has the advanced system of working with actions. REST API works with data through verbs, which are called methods. However, to work with one or a list of entities, such actions may not be enough.
To expand the set of actions, you need to create an action that will work with some aspect of the described model.
For these purposes, there is a standard rest_framework.decorators.action()
, which can also be extended using the scheme.
But for the greater convenience, there is a set of decorator objects in vstutils to eliminate the routine of writing boilerplate code.
The main philosophy for these wrappers is that the developer writes business logic without being distracted by the boilerplate code. Often, most of the errors in the code appear precisely because of the blurry look from the routine writing of the code.
- class vstutils.api.actions.Action(detail=True, methods=None, serializer_class=<class 'vstutils.api.serializers.DataSerializer'>, result_serializer_class=None, query_serializer=None, multi=False, title=None, icons=None, is_list=False, hidden=False, require_confirmation=False, edit_only=False, **kwargs)[source]
Base class of actions. Has minimal of required functionality to create an action and write only business logic. This decorator is suitable in cases where it is not possible to implement the logic using
SimpleAction
or the algorithm is much more complicated than standard CRUD.Examples:
... from vstutils.api.fields import VSTCharField from vstutils.api.serializers import BaseSerializer, DetailsResponseSerializer from vstutils.api.base import ModelViewSet from vstutils.api.actions import Action ... class RequestSerializer(BaseSerializer): data_field1 = ... ... class AuthorViewSet(ModelViewSet): model = ... ... @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) serializer.is_valid(raise_exception=True) return {"detail": "Executed!"}
- Parameters:
detail – 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 record or on a list of records.
methods – List of available HTTP-methods for this action. Default has only POST method.
serializer_class – Request body serializer. Also used for default response.
result_serializer_class – Response body serializer. Required, when request and response has different set of fields.
query_serializer – 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 required type.
multi – Used only with non-GET requests and notify GUI, that this action should be rendered over the selected list items.
title – Title for action in UI. For non-GET actions, title is generated from method’s name.
icons – List of icons for UI button.
is_list – Flag indicating whether the action type is a list or a single entity. Typically used with GET actions.
edit_only – 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 other modifying methods (POST, PUT, PATCH).
require_confirmation – If true user will be asked to confirm action execution on frontend.
kwargs – Set of named arguments for
rest_framework.decorators.action()
.
- class vstutils.api.actions.EmptyAction(**kwargs)[source]
In this case, actions on an object do not require any data and manipulations with them. For such cases, there is a standard method that allows you to simplify the scheme and code to work just with the object.
Optionally, you can also overload the response serializer to notify the interface about the format of the returned data.
Examples:
... from vstutils.api.fields import RedirectIntegerField from vstutils.api.serializers import BaseSerializer from vstutils.api.base import ModelViewSet from vstutils.api.actions import EmptyAction ... class ResponseSerializer(BaseSerializer): id = RedirectIntegerField(operation_name='sync_history') class AuthorViewSet(ModelViewSet): model = ... ... @EmptyAction(result_serializer_class=ResponseSerializer, ...) def sync_data(self, request, *args, **kwargs): ''' Example of action which get object, sync data and redirect user to another view. ''' sync_id = self.get_object().sync().id return {"id": sync_id}
... from django.http.response import FileResponse from vstutils.api.base import ModelViewSet from vstutils.api.actions import EmptyAction ... class AuthorViewSet(ModelViewSet): model = ... ... @EmptyAction(result_serializer_class=ResponseSerializer, ...) def resume(self, request, *args, **kwargs): ''' Example of action which response with generated resume in pdf. ''' instance = self.get_object() return FileResponse( streaming_content=instance.get_pdf(), as_attachment=True, filename=f'{instance.last_name}_{instance.first_name}_resume.pdf' )
- class vstutils.api.actions.SimpleAction(*args, **kwargs)[source]
The idea of this decorator is to get the full CRUD for the instance in a minimum of steps. The instance is the object that was returned from the method being decorated. The whole mechanism is very similar to the standard property decorator, with a description of a getter, setter, and deleter.
If you’re going to create an entry point for working with a single object, then you do not need to define methods. The presence of a getter, setter, and deleter will determine which methods will be available.
In the official documentation of Django, an example is given with moving data that is not important for authorization to the Profile model. To work with such data that is outside the main model, there is this action object, which implements the basic logic in the most automated way.
It covers most of the tasks for working with such data. By default, it has a GET method instead of POST. Also, for better organization of the code, it allows you to change the methods that will be called when modifying or deleting data.
When assigning an action on an object, the list of methods is also filled with the necessary ones.
Examples:
... from vstutils.api.fields import PhoneField from vstutils.api.serializers import BaseSerializer from vstutils.api.base import ModelViewSet from vstutils.api.actions import Action ... class ProfileSerializer(BaseSerializer): phone = PhoneField() class AuthorViewSet(ModelViewSet): model = ... ... @SimpleAction(serializer_class=ProfileSerializer, ...) def profile(self, request, *args, **kwargs): ''' Get profile data to work. ''' return self.get_object().profile @profile.setter def profile(self, instance, request, serializer, *args, **kwargs): instance.save(update_fields=['phone']) @profile.deleter def profile(self, instance, request, serializer, *args, **kwargs): instance.phone = '' instance.save(update_fields=['phone'])
- class vstutils.api.actions.SimpleFileAction(cache_control='max-age=3600', as_attachment=False, *args, **kwargs)[source]
Action for handling file responses. This action always returns a FileResponse.
This class is designed to simplify the process of creating actions that return file responses. It ensures that the file is only sent if it has been modified since the client’s last request, and it sets appropriate headers to facilitate caching and attachment handling.
Examples:
from vstutils.api.base import ModelViewSet from vstutils.api.actions import SimpleFileAction ... class AuthorViewSet(ModelViewSet): model = ... ... @SimpleFileAction() def download_resume(self, request, *args, **kwargs): ''' Get resume file for the author. ''' instance = self.get_object() return instance.resume_file
from vstutils.api.base import ModelViewSet from vstutils.api.actions import SimpleFileAction ... class AuthorViewSet(ModelViewSet): model = ... ... @SimpleFileAction() def download_archives(self, request, *args, **kwargs): ''' Get archive of multiple files for the author. ''' instance = self.get_object() return instance.multiple_files @download_archives.modified_since def modified_since(self, view, obj): ''' Custom modified_since for multiple files. ''' modified_times = [file.storage.get_modified_time(file.name) for file in obj] latest_modified_time = max(modified_times, default=datetime.datetime(1970, 1, 1)) return latest_modified_time @download_archives.pre_data def pre_data(self, view, obj): ''' Custom pre_data for multiple files, creating a zip archive. ''' zip_buffer = BytesIO() with zipfile.ZipFile(zip_buffer, 'w') as zip_file: for file in obj: zip_file.writestr(file.name, file.read()) zip_buffer.seek(0) filename = 'archive.zip' content_type = 'application/zip' return zip_buffer, filename, content_type
- Parameters:
cache_control – Cache-Control header value. Defaults to ‘max-age=3600’.
as_attachment – Boolean indicating if the file should be sent as an attachment. Defaults to False.
kwargs – Additional named arguments for
rest_framework.decorators.action()
.
Filtersets
For greater development convenience, the framework provides additional classes and functions for filtering elements by fields.
- class vstutils.api.filters.DefaultIDFilter(data=None, queryset=None, *, request=None, prefix=None)[source]
Basic filterset to search by id. Provides a search for multiple values separated by commas. Uses
extra_filter()
in fields.
- class vstutils.api.filters.DefaultNameFilter(data=None, queryset=None, *, request=None, prefix=None)[source]
Basic filterset to search by part of name. Uses LIKE DB condition by
name_filter()
.
- class vstutils.api.filters.FkFilterHandler(related_pk='id', related_name='name', pk_handler=<class 'int'>)[source]
Simple handler for filtering by relational fields.
- Parameters:
related_pk (
str
) – Field name of related model’s primary key. Default is ‘id’.related_name (
str
) – Field name of related model’s charfield. Default is ‘name’.pk_handler (
typing.Callable
) – Changes handler for checking value before search. Sends “0” if handler falls. Default is ‘int()’.
- Example:
class CustomFilterSet(filters.FilterSet): author = CharFilter(method=vst_filters.FkFilterHandler(related_pk='pk', related_name='email'))
Where
author
is ForeignKey to User and you want to search by primary key and email.
- vstutils.api.filters.extra_filter(queryset, field, value)[source]
Method for searching values in a comma-separated list.
- Parameters:
queryset (django.db.models.query.QuerySet) – model queryset for filtration.
field (str) – field name in FilterSet. Also supports __not suffix.
value (str) – comma separated list of searching values.
- Returns:
filtered queryset.
- Return type:
- vstutils.api.filters.name_filter(queryset, field, value)[source]
Method for searching by part of name. Uses LIKE DB condition or contains qs-expression.
- Parameters:
queryset (django.db.models.query.QuerySet) – model queryset for filtration.
field (str) – field name in FilterSet. Also supports __not suffix.
value (str) – searching part of name.
- Returns:
filtered queryset.
- Return type:
Responses
DRF provides a standard set of variables whose names correspond to the human-readable name of the HTTP code. For convenience, we have dynamically wrapped it in a set of classes that have appropriate names and additionally provides following capabilities:
String responses are wrapped in json like
{ "detail": "string response" }
.Attribute timings are kept for further processing in middleware.
Status code is set by class name (e.g.
HTTP_200_OK
orResponse200
has code 200).
All classes inherit from:
Middleware
By default, Django supposes that a developer creates Middleware class manually, but it’s often a routine. The vstutils library offers a convenient request handler class for elegant OOP development. Middleware is used to process incoming requests and send responses before they reach final destination.
- class vstutils.middleware.AsyncBaseMiddleware(get_response)[source]
Middleware base class for handling asynchronously:
Incoming requests by
AsyncBaseMiddleware.request_handler()
;Outgoing response before any calling on server by
AsyncBaseMiddleware.get_response_handler()
;Outgoing responses by
AsyncBaseMiddleware.handler()
.
Middleware must be added to MIDDLEWARE list in settings.
- Example:
from vstutils.middleware import AsyncBaseMiddleware from django.http import HttpResponse import aiohttp class CustomMiddleware(AsyncBaseMiddleware): async def request_handler(self, request): # Perform an async HTTP request to get user-agent async with aiohttp.ClientSession() as session: async with session.get('http://httpbin.org/user-agent') as resp: data = await resp.json() request.headers['User-Agent'] = data['user-agent'] return request async def get_response_handler(self, request): if not request.user.is_staff: # Return 403 HTTP status for non-staff users. # This request never gets to any view. return HttpResponse( "Access denied!", content_type="text/plain", status=403 ) return await super().get_response_handler(request) async def handler(self, request, response): # Perform an async HTTP request to get a custom header value async with aiohttp.ClientSession() as session: async with session.get('http://httpbin.org/headers') as resp: data = await resp.json() response['Custom-Header'] = data['headers'].get('Custom-Header', 'Some value') return response
- class vstutils.middleware.BaseMiddleware(get_response)[source]
Middleware base class for handling:
Incoming requests by
BaseMiddleware.request_handler()
;Outgoing response before any calling on server by
BaseMiddleware.get_response_handler()
;Outgoing responses by
BaseMiddleware.handler()
.
Middleware must be added to MIDDLEWARE list in settings.
- Example:
from vstutils.middleware import BaseMiddleware from django.http import HttpResponse class CustomMiddleware(BaseMiddleware): def request_handler(self, request): # Add header to request request.headers['User-Agent'] = 'Mozilla/5.0' return request def get_response_handler(self, request): if not request.user.is_stuff: # Return 403 HTTP status for non-stuff users. # This request never gets in any view. return HttpResponse( "Access denied!", content_type="text/plain", status_code=403 ) return super().get_response_handler(request) def handler(self, request, response): # Add header to response response['Custom-Header'] = 'Some value' return response
- get_response_handler(request)[source]
Entrypoint for breaking or continuing request handling. This function must return django.http.HttpResponse object or result of parent class calling.
Since the release of 5.3, it has been possible to write this method as asynchronous. This should be used in cases where the middleware makes queries to the database or cache. However, such a middleware should be excluded from bulk requests.
Warning
Never do asynchronous middleware in dependent chains. They are designed to send independent requests to external sources.
Set
async_capable
toTrue
andsync_capable
toFalse
for such middleware.- Parameters:
request (django.http.HttpRequest) – HTTP-request object which is wrapped from client request.
- Return type:
- handler(request, response)[source]
The response handler. Method to process responses.
- Parameters:
request (django.http.HttpRequest) – HTTP-request object.
response (django.http.HttpResponse) – HTTP-response object which will be sended to client.
- Returns:
Handled response object.
- Return type:
- request_handler(request)[source]
The request handler. Called before request is handled by a view.
- Parameters:
request (django.http.HttpRequest) – HTTP-request object which is wrapped from client request.
- Returns:
Handled request object.
- Return type:
Filter Backends
Filter Backends are used to modify model queryset.
To create custom filter backend to, (i.g. annotate model queryset), you should inherit from vstutils.api.filter_backends.VSTFilterBackend
and override vstutils.api.filter_backends.VSTFilterBackend.filter_queryset()
and in some cases
vstutils.api.filter_backends.VSTFilterBackend.get_schema_fields()
.
- class vstutils.api.filter_backends.DeepViewFilterBackend[source]
Backend that filters queryset by column from deep_parent_field property of the model. Value for filtering must be provided in query param __deep_parent.
If param is missing then no filtering is applied.
If param is empty value (/?__deep_parent=) then objects with no parent (the value of the field whose name is stored in the property deep_parent_field of the model is None) returned.
This filter backend and nested view is automatically added when model has deep_parent_field property.
- Example:
from django.db import models from vstutils.models import BModel class DeepNestedModel(BModel): name = models.CharField(max_length=10) parent = models.ForeignKey('self', null=True, default=None, on_delete=models.CASCADE) deep_parent_field = 'parent' deep_parent_allow_append = True class Meta: default_related_name = 'deepnested'
In example above if we add this model under path ‘deep’, following views will be created: /deep/ and /deep/{id}/deepnested/.
Filter backend can be used as /deep/?__deep_parent=1 and will return all DeepNestedModel objects whose parent’s primary key is 1.
You can also use generic DRF views, for that you still must set deep_parent_field to your model and manually add DeepViewFilterBackend to filter_backends list.
- class vstutils.api.filter_backends.HideHiddenFilterBackend[source]
Filter Backend that hides all objects with hidden=True from the queryset
- class vstutils.api.filter_backends.SelectRelatedFilterBackend[source]
Filter Backend that will automatically call prefetch_related and select_related on all relations in queryset.
- class vstutils.api.filter_backends.VSTFilterBackend[source]
A base filter backend class to be inherited from. Example:
from django.utils import timezone from django.db.models import Value, DateTimeField from vstutils.api.filter_backends import VSTFilterBackend class CurrentTimeFilterBackend(VSTFilterBackend): def filter_queryset(self, request, queryset, view): return queryset.annotate(current_time=Value(timezone.now(), output_field=DateTimeField()))
In this example Filter Backend annotates time in current timezone to any connected model’s queryset.
In some cases it may be necessary to provide a parameter from a query of request. To define this parameter in the schema, you must overload the get_schema_operation_parameters function and specify a list of parameters to use.
Example:
from django.utils import timezone from django.db.models import Value, DateTimeField from vstutils.api.filter_backends import VSTFilterBackend class ConstantCurrentTimeForQueryFilterBackend(VSTFilterBackend): query_param = 'constant' def filter_queryset(self, request, queryset, view): if self.query_param in request.query_params and request.query_params[self.query_param]: queryset = queryset.annotate(**{ request.query_params[self.query_param]: Value(timezone.now(), output_field=DateTimeField()) }) return queryset def get_schema_operation_parameters(self, view): return [ { "name": self.query_param, "required": False, "in": openapi.IN_QUERY, "description": "Annotate value to queryset", "schema": { "type": openapi.TYPE_STRING, } }, ]
In this example Filter Backend annotates time in current timezone to any connected model’s queryset with field name from query constant.
Celery
Celery is a distributed task queue. It’s used to execute some actions asynchronously in a separate worker. For more details on Celery, check it’s official docs. For Celery related vstutils features to work, you need to specify [rpc] and [worker] sections in settings.ini. Also you need to include extra [rpc] requirements.
Tasks
- class vstutils.tasks.TaskClass[source]
Wrapper for Celery BaseTask class. Usage is same as Celery standard class, but you can execute task without creating instance with
TaskClass.do()
method.Example:
from vstutils.environment import get_celery_app from vstutils.tasks import TaskClass app = get_celery_app() class Foo(TaskClass): def run(*args, **kwargs): return 'Foo task has been executed' app.register_task(Foo())
- Now you can call your task with various methods:
by executing
Foo.do(*args, **kwargs)
get registered task instance like that - app.tasks[‘full_path.to.task.class.Foo’]
Also you can make your registered task periodic, by adding it to CELERY_BEAT_SCHEDULE in settings.py:
CELERY_BEAT_SCHEDULE = { 'foo-execute-every-month': { 'task': 'full_path.to.task.class.Foo', 'schedule': crontab(day_of_month=1), }, }
- classmethod do(*args, **kwargs)[source]
Method which send signal to celery for start remote task execution. All arguments will passed to the task
TaskClass.run()
method.- Return type:
- property name
property for proper Celery task execution, needed for
TaskClass.do()
method to work
- run(*args, **kwargs)
The body of the task executed by workers.
Endpoint
Endpoint view has two purposes: bulk requests execution and providing OpenAPI schema.
Endpoint url is /{API_URL}/endpoint/
, for example value with default settings is /api/endpoint/
.
API_URL
can be changed in settings.py
.
- class vstutils.api.endpoint.EndpointViewSet(**kwargs)[source]
Default API-endpoint viewset.
- get(request)[source]
Returns response with swagger ui or openapi json schema if ?format=openapi
- Parameters:
request (
vstutils.api.endpoint.BulkRequestType
)- Return type:
django.http.response.HttpResponse
- get_client(request)[source]
Returns test client and guarantees that if bulk request comes authenticated than test client will be authenticated with the same user
- Parameters:
request (
vstutils.api.endpoint.BulkRequestType
)- Return type:
vstutils.api.endpoint.BulkClient
- get_serializer(*args, **kwargs)[source]
Return the serializer instance that should be used for validating and deserializing input, and for serializing output.
- Return type:
vstutils.api.endpoint.OperationSerializer
- get_serializer_context(context)[source]
Extra context provided to the serializer class.
- Return type:
- operate(operation_data, context)[source]
Method used to handle one operation and return result of it
- Parameters:
operation_data (
typing.Dict
)context (
typing.Dict
)
- Return type:
- post(request)[source]
Execute transactional bulk request
- Parameters:
request (
vstutils.api.endpoint.BulkRequestType
)- Return type:
- put(request, allow_fail=True)[source]
Execute non transaction bulk request
- Parameters:
request (
vstutils.api.endpoint.BulkRequestType
)- Return type:
- serializer_class
One operation serializer class.
alias of
OperationSerializer
- versioning_class
alias of
QueryParameterVersioning
Bulk requests
Bulk request allows you send multiple requests to api at once, it accepts json list of operations.
Method |
Transactional (all operations in one transaction) |
Synchronous (operations executed one by one in given order) |
---|---|---|
|
NO |
YES |
|
YES |
YES |
|
NO |
NO |
Parameters of one operation (required parameter marked by *):
method
* - http method of requestpath
* - path of request, can bestr
orlist
data
- data to sendquery
- query parameters asstr
let
- string with name of variable (used for access to response result in templates)headers
-dict
with headers which will be sent (key - header’s name, value - header’s value string).version
-str
with specified version of api, if not provided thenVST_API_VERSION
will be used
Warning
In previous versions header’s names must
follow CGI specification
(e.g., CONTENT_TYPE
, GATEWAY_INTERFACE
, HTTP_*
)
Since version 5.3 and after migrate to Django 4 names must follow HTTP specification instead of CGI.
In any request parameter you can insert result value of previous operations
(<<{OPERATION_NUMBER or LET_VALUE}[path][to][value]>>
), for example:
[
{"method": "post", "path": "user", "data": {"name": "User 1"}),
{"method": "delete", "version": "v2", "path": ["user", "<<0[data][id]>>"]}
]
Result of bulk request is json list of objects for operation:
method
- http methodpath
- path of request, always strdata
- data that needs to be sentstatus
- response status code
Transactional bulk request returns 502 BAG GATEWAY
and does rollback after first failed request.
Warning
If you send non-transactional bulk request, you will get 200
status and must
validate statuses on each operation responses.
Error
It is very important to know that each view in a bulk request will use ForcedAuthentication as the successful authenticator. Additionally, the session object will be shared across all requests. In some cases, this allows you to write values into the session between requests without persisting them to storage.
OpenAPI schema
Request on GET /{API_URL}/endpoint/
returns Swagger UI.
Request on GET /{API_URL}/endpoint/?format=openapi
returns OpenAPI schema in json format. Also you can specify required
version of schema using version
query parameter (e.g., GET /{API_URL}/endpoint/?format=openapi&version=v2
).
To change the schema after generating and before sending to user use hooks. Define one or more function, each taking 2 named arguments:
request
- user request object.schema
- ordered dict with OpenAPI schema.
Note
Sometimes hooks may raise an exception; in order to keep a chain of data modification, such exceptions are handled. The changes made to the schema before the exception however, are saved.
- Example hook:
def hook_add_username_to_guiname(request, schema): schema['info']['title'] = f"{request.username} - {schema['info']['title']}"
To connect hook(s) to your app add function import name to the OPENAPI_HOOKS
list in settings.py
OPENAPI_HOOKS = [
'{{appName}}.openapi.hook_add_username_to_guiname',
]
Testing Framework
VST Utils Framework includes a helper in base test case class and improves support for making API requests. That means if you want make bulk request to endpoint you don’t need create and init test client, but just need to call:
endpoint_results = self.bulk([
# list of endpoint requests
])
Creating test case
test.py
module contains test case classes based on vstutils.tests.BaseTestCase
.
At the moment, we officially support two styles of writing tests:
classic and simple query wrappers with run check and
runtime optimized bulk queries with manual value checking.
Simple example with classic tests
For example, if you have api endpoint like /api/v1/project/
and model Project
you can write test case like this:
from vstutils.tests import BaseTestCase
class ProjectTestCase(BaseTestCase):
def setUp(self):
super(ProjectTestCase, self).setUp()
# init demo project
self.initial_project = self.get_model_class('project.Test').objects.create(name="Test")
def tearDown(self)
super(ProjectTestCase, self).tearDown()
# remove it after test
self.initial_project.delete()
def test_project_endpoint(self):
# Test checks that api returns valid values
self.list_test('/api/v1/project/', 1)
self.details_test(
["project", self.initial_project.id],
name=self.initial_project.name
)
# Try to create new projects and check list endpoint
test_data = [
{"name": f"TestProject{i}"}
for i in range(2)
]
id_list = self.mass_create("/api/v1/project/", test_data, 'name')
self.list_test('/api/v1/project/', 1 + len(id_list))
This example demonstrates functionality of default test case class. Default projects are initialized for the fastest and most efficient result. We recommend to divide tests for different entities into different classes. This example demonstrate classic style of testing, but you can use bulks in your test cases.
Bulk requests in tests
Bulk query system is well suited for testing and executing valid queries. Previous example could be rewritten as follows:
from vstutils.tests import BaseTestCase
class ProjectTestCase(BaseTestCase):
def setUp(self):
super(ProjectTestCase, self).setUp()
# init demo project
self.initial_project = self.get_model_class('project.Test').objects.create(name="Test")
def tearDown(self)
super(ProjectTestCase, self).tearDown()
# remove it after test
self.initial_project.delete()
def test_project_endpoint(self):
test_data = [
{"name": f"TestProject{i}"}
for i in range(2)
]
bulk_data = [
{"method": "get", "path": ["project"]},
{"method": "get", "path": ["project", self.initial_project.id]}
]
bulk_data += [
{"method": "post", "path": ["project"], "data": i}
for i in test_data
]
bulk_data.append(
{"method": "get", "path": ["project"]}
)
results = self.bulk_transactional(bulk_data)
self.assertEqual(results[0]['status'], 200)
self.assertEqual(results[0]['data']['count'], 1)
self.assertEqual(results[1]['status'], 200)
self.assertEqual(results[1]['data']['name'], self.initial_project.name)
for pos, result in enumerate(results[2:-1]):
self.assertEqual(result['status'], 201)
self.assertEqual(result['data']['name'], test_data[pos]['name'])
self.assertEqual(results[-1]['status'], 200)
self.assertEqual(results[-1]['data']['count'], 1 + len(test_data))
In this case, you have more code, but your tests are closer to GUI workflow,
because vstutils-projects uses /api/endpoint/
for requests.
Either way, bulk queries are much faster due to optimization;
Testcase execution time is less comparing to non-bulk requests.
Test case API
- class vstutils.tests.BaseTestCase(methodName='runTest')[source]
Main test case class extends
django.test.TestCase
.- assertCheckDict(first, second, msg=None)[source]
Fail if the two fields in dicts are unequal as determined by the ‘==’ operator. Checks if first not contains or not equal field in second
- Parameters:
first (
typing.Dict
)second (
typing.Dict
)msg (
str
)
- assertCount(iterable, count, msg=None)[source]
Calls
len()
overiterable
and checks equality withcount
.- Parameters:
iterable (
typing.Sized
) – any iterable object which could be sended tolen()
.count (
int
) – expected result.msg (
typing.Any
) – error message
- assertRCode(resp, code=200, *additional_info)[source]
Fail if response code is not equal. Message is response body.
- Parameters:
resp (django.http.HttpResponse) – response object
code (int) – expected code
- bulk(data, code=200, **kwargs)[source]
Makes non-transactional bulk request and asserts status code (default is 200)
- Parameters:
data (
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]) – request datacode (
int
) – http status to assertkwargs – named arguments for
get_result()
- Return type:
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
,typing.Dict
,typing.Sequence
[typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]]]- Returns:
bulk response
- bulk_transactional(data, code=200, **kwargs)[source]
Make transactional bulk request and assert status code (default is 200)
- Parameters:
data (
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]) – request datacode (
int
) – http status to assertkwargs – named arguments for
get_result()
- Return type:
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
,typing.Dict
,typing.Sequence
[typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]]]- Returns:
bulk response
- client_token_app_id = 'simple-client-id'
oAuth2 client id
- client_token_grant_type = 'password'
oAuth2 grant type
- client_token_scopes = 'openid read write'
oAuth2 scopes
- details_test(url, **kwargs)[source]
Test for get details of model. If you setup additional named arguments, the method check their equality with response data. Uses
get_result()
method.- Parameters:
url – url to detail record. For example:
/api/v1/project/1/
(where1
is uniq id of project). You can useget_url()
for building url.kwargs – params that’s should be checked (key - field name, value - field value).
- endpoint_call(data=None, method='get', code=200, **kwargs)[source]
Makes request to endpoint and asserts response status code if specified (default is 200). Uses
get_result()
method for execution.- Parameters:
data (
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]) – request datamethod (
str
) – http request methodcode (
int
) – http status to assertquery – dict with query data (working only with get)
- Return type:
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
,typing.Dict
,typing.Sequence
[typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]]]- Returns:
bulk response
- endpoint_schema(**kwargs)[source]
Make request to schema. Returns dict with swagger data.
- Parameters:
version – API version for schema parser.
- get_count(model, **kwargs)[source]
Simple wrapper over
get_model_filter()
which returns counter of items.- Parameters:
model (str,django.db.models.Model) – string which contains model name (if attribute
model
is set to the test case class), module import,app.ModelName
ordjango.db.models.Model
.kwargs – named arguments to
django.db.models.query.QuerySet.filter()
.
- Returns:
number of instances in database.
- Return type:
- get_model_class(model)[source]
Getting model class by string or return model arg.
- Parameters:
model (str,django.db.models.Model) – string which contains model name (if attribute
model
is set to the test case class), module import,app.ModelName
ordjango.db.models.Model
.- Returns:
Model class.
- Return type:
- get_model_filter(model, **kwargs)[source]
Simple wrapper over
get_model_class()
which returns filtered queryset from model.- Parameters:
model (str,django.db.models.Model) – string which contains model name (if attribute
model
is set to the test case class), module import,app.ModelName
ordjango.db.models.Model
.kwargs – named arguments to
django.db.models.query.QuerySet.filter()
.
- Return type:
- get_result(rtype, url, code=None, *args, **kwargs)[source]
Executes and tests response code on request with returning parsed result of request. The method uses the following procedure:
Test client authorization (with
user
which creates insetUp()
).Executing a request (sending args and kwargs to request method).
Parsing the result (converts json string to python-object).
Checking the http status code with
assertRCode()
(if you have not specified it, the code will be selected in accordance with the request method from the standard setstd_codes
).Logout client.
Return parsed result.
- Parameters:
rtype – request type (methods from Client cls): get, post etc.
url – requested url string or tuple for
get_url()
. You can useget_url()
for url building or setup it as full string.code (
int
) – expected return code from request.relogin – execute force login and logout on each call. Default is
True
.args – extra-args for Client class request method.
kwargs – extra-kwargs for Client class request method.
- Return type:
typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
,typing.Dict
,typing.Sequence
[typing.Union
[typing.List
[typing.Dict
[str
,typing.Any
]],str
,bytes
,bytearray
]]]- Returns:
result of request.
- get_url(*items)[source]
Function for creating url path based on
VST_API_URL
andVST_API_VERSION
settings. Without arguments returns path to default version of api.- Return type:
- Returns:
string like
/api/v1/.../.../
where...
is args of function.
- list_test(url, count)[source]
Test for get list of models. Checks only list count. Uses
get_result()
method.- Parameters:
url – url to abstract layer. For example:
/api/v1/project/
. You can useget_url()
for building url.count – count of objects in DB.
- models = None
Attribute with default project models module.
- classmethod patch(*args, **kwargs)[source]
Simple
unittest.mock.patch()
class-method wrapper.- Return type:
- classmethod patch_field_default(model, field_name, value)[source]
This method helps to path default value in the model’s field. It’s very useful for DateTime fields where
django.utils.timezone.now()
is used in defaults.- Parameters:
model (
django.db.models.base.Model
)field_name (
str
)value (
typing.Any
)
- Return type:
- server_class
oAuth2 server class
alias of
AuthorizationServer
-
std_codes:
typing.Dict
[str
,int
] = {'delete': 204, 'get': 200, 'patch': 200, 'post': 201} Default http status codes for different http methods. Uses in
get_result()
- class user_as(testcase, user)[source]
Context for execute bulk or something as user. The context manager overrides
self.user
in TestCase and revert this changes on exit.- Parameters:
user (django.contrib.auth.models.AbstractUser) – new user object for execution.
Utils
This is tested set of development utilities. Utilities include a collection of code that will be useful in one way or another for developing the application. Vstutils uses mosts of these functions under the hood.
- class vstutils.utils.BaseEnum(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
BaseEnum extends
enum.Enum
class and used to create enum-like objects that can be used in django serializers or django models.- Example:
from vstutils.models import BModel class ItemClasses(BaseEnum): FIRST = BaseEnum.SAME SECOND = BaseEnum.SAME THIRD = BaseEnum.SAME class MyDjangoModel(BModel): item_class = models.CharField(max_length=ItemClasses.max_len, choices=ItemClasses.to_choices()) @property def is_second(self): # Function check is item has second class of instance return ItemClasses.SECOND.is_equal(self.item_class)
Note
For special cases, when value must be in lower or upper case, you can setup value as
BaseEnum.LOWER
orBaseEnum.UPPER
. But in default cases we recommend useBaseEnum.SAME
for memory optimization.
- class vstutils.utils.BaseVstObject[source]
Default mixin-class for custom objects which needed to get settings and cache.
- class vstutils.utils.Executor(stdout=-1, stderr=-2, **environ_variables)[source]
Command executor with realtime output write and line handling. By default and by design executor initialize string attribute
output
which will be modified by+=
operator with new lines byExecutor.write_output()
procedure. Override the method if you want change behavior.Executor class supports periodically (0.01 sec) handling process and execute some checks by overriding
Executor.working_handler()
procedure method. If you want disable this behavior override the method by None value or useUnhandledExecutor
.- Parameters:
environ_variables (
str
)
- exception CalledProcessError(returncode, cmd, output=None, stderr=None)
Raised when run() is called with check=True and the process returns a non-zero exit status.
- Attributes:
cmd, returncode, stdout, stderr, output
- property stdout
Alias for output attribute, to match stderr
- async aexecute(cmd, cwd, env=None)[source]
Executes commands and outputs its result. Asynchronous implementation.
- Parameters:
cmd – – list of cmd command and arguments
cwd – – workdir for executions
env – – extra environment variables which overrides defaults
- Returns:
– string with full output
- execute(cmd, cwd, env=None)[source]
Executes commands and outputs its result.
- Parameters:
cmd – – list of cmd command and arguments
cwd – – workdir for executions
env – – extra environment variables which overrides defaults
- Returns:
– string with full output
- async post_execute(cmd, cwd, env, return_code)[source]
Runs after execution end.
- Parameters:
cmd – – list of cmd command and arguments
cwd – – workdir for executions
env – – extra environment variables which overrides defaults
return_code – – return code of executed process
- async pre_execute(cmd, cwd, env)[source]
Runs before execution starts.
- Parameters:
cmd – – list of cmd command and arguments
cwd – – workdir for executions
env – – extra environment variables which overrides defaults
- async working_handler(proc)[source]
Additional handler for executions.
- Parameters:
proc (asyncio.subprocess.Process) – running process
- class vstutils.utils.KVExchanger(key, timeout=None)[source]
Class for transmit data using key-value fast (cache-like) storage between services. Uses same cache-backend as Lock.
- class vstutils.utils.Lock(id, payload=1, repeat=1, err_msg='', timeout=None)[source]
Lock class for multi-jobs workflow. Based on
KVExchanger
. The Lock allows only one thread to enter the part that’s locked and shared between apps using one locks cache (see also [locks]).- Parameters:
Note
Used django.core.cache lib and settings in settings.py
Have Lock.SCHEDULER and Lock.GLOBAL id
- Example:
from vstutils.utils import Lock with Lock("some_lock_identifier", repeat=30, err_msg="Locked by another proccess") as lock: # where # ``"some_lock_identifier"`` is unique id for lock and # ``30`` seconds lock is going wait until another process will release lock id. # After 30 sec waiting lock will raised with :class:`.Lock.AcquireLockException` # and ``err_msg`` value as text. some_code_execution() # ``lock`` object will has been automatically released after # exiting from context.
- Another example without context manager:
from vstutils.utils import Lock # locked block after locked object created lock = Lock("some_lock_identifier", repeat=30, err_msg="Locked by another proccess") # deleting of object calls ``lock.release()`` which release and remove lock from id. del lock
- class vstutils.utils.ModelHandlers(type_name, err_message=None)[source]
Handlers for some models like ‘INTEGRATIONS’ or ‘REPO_BACKENDS’. Based on
ObjectHandlers
but more specific for working with models. All handlers backends get by first argument model object.Attributes:
- Parameters:
- get_object(name, obj)[source]
- Parameters:
name – – string name of backend
name – str
obj (django.db.models.Model) – – model object
- Returns:
backend object
- Return type:
- class vstutils.utils.ObjectHandlers(type_name, err_message=None)[source]
Handlers wrapper for get objects from some settings structure.
- Example:
from vstutils.utils import ObjectHandlers ''' In `settings.py` you should write some structure: SOME_HANDLERS = { "one": { "BACKEND": "full.python.path.to.module.SomeClass" }, "two": { "BACKEND": "full.python.path.to.module.SomeAnotherClass", "OPTIONS": { "some_named_arg": "value" } } } ''' handlers = ObjectHandlers('SOME_HANDLERS') # Get class handler for 'one' one_backend_class = handlers['one'] # Get object of backend 'two' two_obj = handlers.get_object() # Get object of backend 'two' with overriding constructor named arg two_obj_overrided = handlers.get_object(some_named_arg='another_value')
- Parameters:
type_name (str) – type name for backends.Like name in dict.
- class vstutils.utils.Paginator(qs, chunk_size=None)[source]
Class for fragmenting the query for small queries.
- class vstutils.utils.SecurePickling(secure_key=None)[source]
Secured pickle wrapper by Vigenère cipher.
Warning
Do not use it with untrusted transport anyway.
- Example:
from vstutils.utils import SecurePickling serializer = SecurePickling('password') # Init secret object a = {"key": "value"} # Serialize object with secret key pickled = serializer.dumps(a) # Deserialize object unpickled = serializer.loads(pickled) # Check, that object is correct assert a == unpickled
- class vstutils.utils.URLHandlers(type_name='URLS', *args, **kwargs)[source]
Object handler for GUI views. Uses GUI_VIEWS from settings.py. Based on
ObjectHandlers
but more specific to urlpatterns.- Example:
from vstutils.utils import URLHandlers # By default gets from `GUI_VIEWS` in `settings.py` urlpatterns = list(URLHandlers())
- Parameters:
type_name – type name for backends.Like name in dict.
- class vstutils.utils.UnhandledExecutor(stdout=-1, stderr=-2, **environ_variables)[source]
Class based on
Executor
but disables working_handler.- Parameters:
environ_variables (
str
)
- vstutils.utils.add_in_vary(headers, value)[source]
Adds provided value to Vary header if not added already
- class vstutils.utils.apply_decorators(*decorators)[source]
Decorator which apply list of decorators on method or class.
- Example:
from vstutils.utils import apply_decorators def decorator_one(func): print(f"Decorated {func.__name__} by first decorator.") return func def decorator_two(func): print(f"Decorated {func.__name__} by second decorator.") return func @apply_decorators(decorator_one, decorator_two) def decorated_function(): # Function decorated by both decorators. print("Function call.")
- vstutils.utils.check_request_etag(request, etag_value, header_name='If-None-Match', operation_handler=<slot wrapper '__eq__' of 'str' objects>)[source]
The function plays a crucial role within the context of the ETag mechanism, providing a flexible way to validate client-side ETags against the server-side version for both cache validation and ensuring data consistency in web applications. It supports conditional handling of HTTP requests based on the match or mismatch of ETag values, accommodating various scenarios such as cache freshness checks and prevention of concurrent modifications.
- Parameters:
request (
rest_framework.request.Request
) – The HTTP request object containing the client’s headers, from which the ETag for comparison is retrieved.etag_value (
str
) – The server-generated ETag value that represents the current state of the resource. This unique identifier is recalculated whenever the resource’s content changes.header_name (
str
) – Specifies the HTTP header to look for the client’s ETag. Defaults to “If-None-Match”, commonly used in GET requests for cache validation. For operations requiring confirmation that the client is acting on the latest version of a resource (e.g., PUT or DELETE), “If-Match” should be used instead.operation_handler – A function to compare the ETags. By default, this is set to
str.__eq__
, which checks for an exact match between the client’s and server’s ETags, suitable for validating caches withIf-None-Match
. To handleIf-Match
scenarios, where the operation should proceed only if the ETags do not match, indicating the resource has been modified,str.__ne__
(not equal) can be used as the operation handler. This flexibility allows for precise control over how and when clients are allowed to read from or write to resources based on their version.
- Returns:
Returns a tuple containing the server’s ETag and a boolean flag. The flag is
True
if the operation handler condition between the server’s and client’s ETag is met, indicating the request should proceed based on the matching logic defined by the operation handler; otherwise, it returnsFalse
.
- class vstutils.utils.classproperty(fget, fset=None)[source]
Decorator which makes class method as class property.
- Example:
from vstutils.utils import classproperty class SomeClass(metaclass=classproperty.meta): # Metaclass is needed for set attrs in class # instead of and not only object. some_value = None @classproperty def value(cls): return cls.some_value @value.setter def value(cls, new_value): cls.some_value = new_value
- Parameters:
fget – function for getting an attribute value.
fset – function for setting an attribute value.
- vstutils.utils.create_view(model, **meta_options)[source]
A simple function for getting the generated view by standard means, but with overloaded meta-parameters. This method can completely get rid of the creation of proxy models.
- Example:
from vstutils.utils import create_view from .models import Host # Host model has full :class:`vstutils.api.base.ModelViewSet` view. # For overriding and create simple list view just setup this: HostListViewSet = create_view( HostList, view_class='list_only' )
Note
This method is also recommended in cases where there is a problem of recursive imports.
Warning
This function is oldstyle and will be deprecated in future versions. Use native call of method
vstutils.models.BModel.get_view_class()
.- Parameters:
model (Type[vstutils.models.BaseModel]) – Model class with .get_view_class method. This method also has
vstutils.models.BModel
.- Return type:
- vstutils.utils.deprecated(func)[source]
This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.
- Parameters:
func – any callable that will be wrapped and will issue a deprecation warning when called.
- vstutils.utils.lazy_translate(text)[source]
The
lazy_translate
function has the same behavior astranslate()
, but wraps it in a lazy promise.This is very useful, for example, for translating error messages in class attributes before the language code is known.
- Parameters:
text – Text message which should be translated.
- vstutils.utils.list_to_choices(items_list, response_type=<class 'list'>)[source]
Method to create django model choices from flat list of values.
- Parameters:
items_list – list of flat values.
response_type – casting type of returned mapping
- Returns:
list of tuples from items_list values
- class vstutils.utils.model_lock_decorator(**kwargs)[source]
Decorator for functions where ‘pk’ kwarg exist for lock by id.
Warning
On locked error raised
Lock.AcquireLockException
Method must have and called with
pk
named arg.
- class vstutils.utils.raise_context_decorator_with_default(*args, **kwargs)[source]
Context for exclude errors and return default value.
- Example:
from yaml import load from vstutils.utils import raise_context_decorator_with_default @raise_context_decorator_with_default(default={}) def get_host_data(yaml_path, host): with open(yaml_path, 'r') as fd: data = load(fd.read(), Loader=Loader) return data[host] # This decorator used when you must return some value even on error # In log you also can see traceback for error if it occur def clone_host_data(host): bs_data = get_host_data('inventories/aws/hosts.yml', 'build_server') ...
- vstutils.utils.raise_misconfiguration(ok, message=None)[source]
Helper function that raises an ImproperlyConfigured exception if a condition is not met.
This function acts as a replacement for the assert statement, providing clearer error handling in cases where the application configuration is incorrect.
- Parameters:
ok (Any) – A value of any type that can be evaluated as a boolean. If the boolean evaluation returns False, the exception will be raised.
message (str, optional) – An optional message to include in the exception. If not provided, the exception will be raised without a message.
- Raises:
ImproperlyConfigured – Raised if the boolean evaluation of the ok parameter is False, indicating a misconfiguration in the application.
- Returns:
This function does not return any value. It either passes silently or raises an exception.
- Return type:
None
- class vstutils.utils.redirect_stdany(new_stream=<_io.StringIO object>, streams=None)[source]
Context for redirect any output to own stream.
Note
On context returns stream object.
On exit returns old streams.
- vstutils.utils.send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None, **kwargs)[source]
Wrapper over
django.core.mail.send_mail()
which provide additional named arguments.
- vstutils.utils.send_template_email(sync=False, **kwargs)[source]
Function executing sync or async email sending; according sync argument and settings variable “RPC_ENABLED”. If you don’t set settings for celery or don’t have celery it sends synchronously mail. If celery is installed and configured and sync argument of the function is set to False, it sends asynchronously email.
- Parameters:
sync – argument for determining how send email, asynchronously or synchronously
subject – mail subject.
email – list of strings or single string, with email addresses of recipients
template_name – relative path to template in templates directory, must include extension in file name.
context_data – dictionary with context for rendering message template.
- vstutils.utils.send_template_email_handler(subject, email_from, email, template_name, context_data=None, **kwargs)[source]
Function for email sending. The function convert recipient to list and set context before sending if it possible.
- Parameters:
subject – mail subject.
email_from – sender that be setup in email.
email – list of strings or single string, with email addresses of recipients
template_name – relative path to template in templates directory, must include extension in file name.
context_data – dictionary with context for rendering message template.
kwargs – additional named arguments for send_mail
- Returns:
Number of emails sent.
- class vstutils.utils.tmp_file(data='', mode='w', bufsize=-1, **kwargs)[source]
Temporary file with name generated and auto removed on close.
Attributes:
- Parameters:
- class vstutils.utils.tmp_file_context(*args, **kwargs)[source]
Context object for work with tmp_file. Auto close on exit from context and remove if file still exist.
This context manager over
tmp_file
- vstutils.utils.translate(text)[source]
The
translate
function supports translation message dynamically with standard i18n vstutils’es mechanisms usage.Uses
django.utils.translation.get_language()
to get the language code and tries to get the translation from the list of available ones.- Parameters:
text – Text message which should be translated.
Integrating Web Push Notifications
Web push notifications are an effective way to engage users with real-time messaging. To integrate web push notifications in your VSTUtils project, follow these steps:
Configuration: First, include the
vstutils.webpush
module in theINSTALLED_APPS
section of yoursettings.py
file. This enables the web push functionality provided by VSTUtils. Additionally, configure the necessary settings as described in the web push settings section (see here for details).Creating Notifications: To create a web push notification, you need to define a class that inherits from either
vstutils.webpush.BaseWebPush
orvstutils.webpush.BaseWebPushNotification
. VSTUtils automatically detects and utilizes web push classes defined in thewebpushes
module of allINSTALLED_APPS
. Below is an example that illustrates how to implement custom web push classes:1from vstutils.api.models import Language 2from vstutils.webpush.base import BaseWebPush, BaseWebPushNotification 3from vstutils.webpush.models import WebPushDeviceSubscription, WebPushNotificationSubscription 4 5 6class TestWebPush(BaseWebPush): 7 """ 8 Webpush that is sent to all subscribed users 9 """ 10 11 def get_subscriptions(self): 12 return WebPushDeviceSubscription.objects.filter( 13 user_id__in=WebPushNotificationSubscription.objects.filter( 14 type=self.get_key(), 15 enabled=True, 16 ).values('user_id'), 17 ) 18 19 def get_payload(self, lang: Language): 20 return {"some": "data", "lang": lang.code} 21 22 23class TestNotification(BaseWebPushNotification): 24 """ 25 Webpush notification that is sent only to selected users 26 """ 27 28 def __init__(self, name: str, user_id: int): 29 self.name = name 30 self.user_id = user_id 31 self.message = f"Hello {self.name}" 32 33 def get_users_ids(self): 34 return (self.user_id,) 35 36 def get_notification(self, lang: Language): 37 return { 38 "title": self.message, 39 "options": { 40 "body": "Test notification body", 41 "data": {"url": "/"}, 42 }, 43 } 44 45 46class StaffOnlyNotification(BaseWebPushNotification): 47 """ 48 Webpush notification that only staff user can subscribe to. 49 """ 50 51 @staticmethod 52 def is_available(user): 53 return user.is_staff 54 55 56class NotificationFromOtherApp(BaseWebPushNotification): 57 """ 58 Webpush notification from other project that should not be available in ui 59 """ 60 project = 'other_project'
This example contains three classes:
TestWebPush: Sends notifications to all subscribed users.
TestNotification: Targets notifications to specific users.
StaffOnlyNotification: Restricts notifications to staff users only. Sometimes you may want to allow only some users to subscribe on specific notifications.
Sending Notifications: To dispatch a web push notification, invoke the
send
orsend_in_task
method on an instance of your web push class. For instance, to send a notification using TestNotification, you can do the following:from test_proj.webpushes import TestNotification # Sending a notification immediately (synchronously) TestNotification(name='Some user', user_id=1).send() # Sending a notification as a background task (asynchronously) TestNotification.send_in_task(name='Some user', user_id=1)
Warning
The asynchronous sending of web push notifications (using methods like send_in_task
) requires a configured Celery setup
in your project, as it relies on Celery tasks “under the hood”.
Ensure that Celery is properly set up and running to utilize asynchronous notification dispatching.
By following these steps, you can fast integrate and utilize web push notifications in projects with VSTUtils.
Troubleshooting
Vstutils makes some errors more readable for common users and provides special error codes for administration to simplify troubleshooting.
VE100-VE199
- Database related errors.VE100
- Integrity error code. Used whendjango.db.utils.IntegrityError
appears.