djangorestframework-dataclasses


Namedjangorestframework-dataclasses JSON
Version 1.3.0 PyPI version JSON
download
home_page
SummaryA dataclasses serializer for Django REST Framework
upload_time2023-08-21 20:04:48
maintainer
docs_urlNone
author
requires_python>=3.7
licenseCopyright (c) 2019-2021, Oxan van Leeuwen Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            Dataclasses serializer
======================

A `dataclasses <https://docs.python.org/3/library/dataclasses.html>`__ serializer for the `Django REST Framework
<http://www.django-rest-framework.org/>`__.

.. image:: https://github.com/oxan/djangorestframework-dataclasses/workflows/CI/badge.svg
   :target: https://github.com/oxan/djangorestframework-dataclasses/actions?query=workflow%3ACI
.. image:: https://codecov.io/gh/oxan/djangorestframework-dataclasses/branch/master/graph/badge.svg
   :target: https://codecov.io/gh/oxan/djangorestframework-dataclasses
.. image:: https://badge.fury.io/py/djangorestframework-dataclasses.svg
   :target: https://badge.fury.io/py/djangorestframework-dataclasses
.. image:: https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=success
   :target: https://github.com/sponsors/oxan

|

.. contents:: :local:

Requirements
------------

* Python (3.8+)
* Django (3.2+)
* Django REST Framework (3.11+)

These are the supported Python and package versions. Older versions will probably work as well, but aren't tested.

Installation
------------

::

    $ pip install djangorestframework-dataclasses

This package follows `semantic versioning`_. See `CHANGELOG`_ for breaking changes and new features, and `LICENSE`_ for
the complete license (BSD-3-clause).

.. _`semantic versioning`: https://semver.org/
.. _`CHANGELOG`: https://github.com/oxan/djangorestframework-dataclasses/blob/master/CHANGELOG.rst
.. _`LICENSE`: https://github.com/oxan/djangorestframework-dataclasses/blob/master/LICENSE

Basic usage
-----------

The package provides the ``DataclassSerializer`` serializer, defined in the ``rest_framework_dataclasses.serializers``
namespace.

.. code:: Python

    from rest_framework_dataclasses.serializers import DataclassSerializer

This serializer provides a shortcut that lets you automatically create a ``Serializer`` class with fields that
correspond to the fields on a dataclass. In usage, the ``DataclassSerializer`` is the same as a regular ``Serializer``
class, except that:

* It will automatically generate fields for you, based on the declaration in the dataclass.
* To make this possible it requires that a ``dataclass`` property is specified in the ``Meta`` subclass, with as value
  a dataclass that has type annotations.
* It includes default implementations of ``.create()`` and ``.update()``.

For example, define a dataclass as follows:

.. code:: Python

    @dataclass
    class Person:
        name: str
        email: str
        alive: bool
        gender: typing.Literal['male', 'female']
        birth_date: typing.Optional[datetime.date]
        phone: typing.List[str]
        movie_ratings: typing.Dict[str, int]

The serializer for this dataclass can now trivially be defined without having to duplicate all fields:

.. code:: Python

    class PersonSerializer(DataclassSerializer):
        class Meta:
            dataclass = Person

    # is equivalent to
    class PersonSerializer(Serializer):
        name = fields.CharField()
        email = fields.CharField()
        alive = fields.BooleanField()
        gender = fields.ChoiceField(choices=['male', 'female'])
        birth_date = fields.DateField(allow_null=True)
        phone = fields.ListField(child=fields.CharField())
        movie_ratings = fields.DictField(child=fields.IntegerField())

You can add extra fields or override default fields by declaring them explicitly on the class, just as you would for a
regular ``Serializer`` class. This allows to specify extra field options or change a field type.

.. code:: Python

    class PersonSerializer(Serializer):
        email = fields.EmailField()

        class Meta:
            dataclass = Person

Dataclass serializers behave in the same way and can be used in the same places as the built-in serializers from Django
REST Framework: you can retrieve the serialized representation using the ``.data`` property, and the deserialized
dataclass instance using the ``.validated_data`` property. Furthermore, the ``save()`` method is implemented to create
or update an existing dataclass instance. You can find more information on serializer usage in the
`Django REST Framework <https://www.django-rest-framework.org/api-guide/serializers/>`__ documentation.

Note that this usage pattern is very similar to that of the built-in ``ModelSerializer``. This is intentional, with the
whole API modelled after that of ``ModelSerializer``. Most features and behaviour known from ``ModelSerializer`` applies
to dataclass serializers as well.

Field mapping
-------------

Currently, automatic field generation is supported for the following types and their subclasses:

* ``str``, ``bool``, ``int`` and ``float``.
* ``date``, ``datetime``, ``time`` and ``timedelta`` from the ``datetime`` package.
* ``decimal.Decimal`` (``max_digits`` and ``decimal_places`` default to ``None`` and ``2`` respectively).
* ``uuid.UUID``
* ``enum.Enum`` (mapped to a ``EnumField``)
* ``typing.Iterable`` (including ``typing.List`` and `PEP 585`_-style generics such as ``list[int]``).
* ``typing.Mapping`` (including ``typing.Dict`` and `PEP 585`_-style generics such as ``dict[str, int]``).
* ``typing.Literal`` (mapped to a ``ChoiceField``).
* ``typing.Union`` (mapped to a ``UnionField``, including `PEP 604`_-style unions such as ``str | int``, see
  `UnionField`_ section below for more information).
* ``django.db.Model``

The serializer also supports type variables that have an upper bound or are constrained.

Customize field generation
--------------------------

The auto-generated serializer fields are configured based on type qualifiers in the dataclass (these can be mixed):

* Fields with a default value (factory) are marked as optional on the serializer (``required=False``). This means that
  these fields don't need to be supplied during deserialization.

* Fields marked as nullable through ``typing.Optional``, ``typing.Union[X, None]`` or ``X | None`` (`PEP 604`_) are
  marked as nullable on the serializer (``allow_null=True``). This means that ``None`` is accepted as a valid value
  during deserialization.

* Fields marked as final through ``typing.Final`` (as in `PEP 591`_) are marked as read-only on the serializer
  (``read_only=True``).

.. code:: Python

    @dataclass
    class Person:
        birth_date: typing.Optional[datetime.date]
        alive: bool = True
        species: typing.Final[str] = 'Human'

    # the autogenerated serializer will be equal to
    class PersonSerializer(Serializer):
        birth_date = fields.DateField(allow_null=True)
        alive = fields.BooleanField(required=False)
        species = fields.CharField(read_only=True)

Besides overriding fields by declaring them explicitly on the serializer, you can also change or override the generated
serializer field using metadata on the dataclass field. Currently, two keys are recognized in this dictionary:

* ``serializer_field`` can be used to replace the auto-generated field with a user-supplied one. Should contain an
  instance of a field, not a field type.

* ``serializer_kwargs`` can be used to specify arbitrary additional keyword arguments for the generated field. Manually
  specified arguments will have precedence over generated arguments (so e.g. by supplying ``{required: True}``, a field
  with a default value can be made required).

.. code:: Python

    @dataclasses.dataclass
    class Person:
        email: str = dataclasses.field(metadata={'serializer_field': fields.EmailField()})
        age: int = dataclasses.field(metadata={'serializer_kwargs': {'min_value': 0}})

    # the autogenerated serializer will be equal to
    class PersonSerializer(Serializer):
        email = fields.EmailField()
        age = fields.IntegerField(min_value=0)

To further customize the serializer, the ``DataclassSerializer`` accepts the following options in the ``Meta``
subclass. All options have the same behaviour as the identical options in ``ModelSerializer``.

* ``dataclass`` specifies the type of dataclass used by the serializer. This is equivalent to the ``model`` option in
  ``ModelSerializer``.

* ``fields`` and ``exclude`` can be used to specify which fields should respectively be included and excluded in the
  serializer. These cannot both be specified.

  The ``fields`` option accepts the magic value ``__all__`` to specify that all fields on the dataclass should be used.
  This is also the default value, so it is not mandatory to specify either ``fields`` or ``exclude``.

* ``read_only_fields`` can be used to mark a subset of fields as read-only.

* ``extra_kwargs`` can be used to specify arbitrary additional keyword arguments on fields. This can be useful to
  extend or change the autogenerated field without explicitly declaring the field on the serializer. This option should
  be a dictionary, mapping field names to a dictionary of keyword arguments.

  If the autogenerated field is a composite field (a list or dictionary), the arguments are applied to the composite
  field. To add keyword arguments to the composite field's child field (that is, the field used for the items in the
  list or dictionary), they should be specified as a nested dictionary under the ``child_kwargs`` name (see
  `Nested dataclasses`_ section below for an example).

  .. code:: Python

    class PersonSerializer(DataclassSerializer):
        class Meta:
            extra_kwargs = {
                'height': { 'decimal_places': 1 },
                'movie_ratings': { 'child_kwargs': { 'min_value': 0, 'max_value': 10 } }
            }

* ``validators`` functionality is unchanged.

* ``depth`` (as known from ``ModelSerializer``) is not supported, it will always nest infinitely deep.

Changing default behaviour
~~~~~~~~~~~~~~~~~~~~~~~~~~

Additionally, it is possible to change the default behaviour of the ``DataclassSerializer`` by setting one of these
properties on the class:

* The ``serializer_field_mapping`` property contains a dictionary that maps types to REST framework serializer classes.
  You can override or extend this mapping to change the serializer field classes that are used for fields based on
  their type. This dictionary also accepts dataclasses as keys to change the serializer used for a nested dataclass.

* The ``serializer_related_field`` property is the serializer field class that is used for relations to models.

* The ``serializer_union_field`` property is the serializer field class that is used for union types.

* The ``serializer_dataclass_field`` property is the serializer field class that is used for nested dataclasses. Note
  that since Python process the class body before it defines the class, this property is implemented using the
  `property decorator`_ to allow it to reference the containing class.

Finally, you can create a subclass that overrides methods of the ``DataclassSerializer``. The field generation is
controlled by the following methods, which are considered a stable part of the API:

* The ``build_unknown_field()`` method is called to create serializer fields for dataclass fields that are not
  understood. By default this just throws an error, but you can extend this with custom logic to create serializer
  fields.

* The ``build_property_field()`` method is called to create serializer fields for methods. By default this creates a
  read-only field with the method return value.

* The ``build_standard_field()``, ``build_relational_field()``, ``build_dataclass_field()``, ``build_union_field()``,
  ``build_enum_field()``, ``build_literal_field()`` and ``build_composite_field()`` methods are used to process
  respectively fields, nested models, nested dataclasses, union types, enums, literals, and lists or dictionaries. These
  can be overridden to change the field generation logic.

Note that when creating a subclass of ``DataclassSerializer``, most likely you will want to set the
``serializer_dataclass_field`` property to the subclass, so that any nested dataclasses are serialized using the
subclass as well.

.. code:: Python

    class CustomDataclassSerializer(DataclassSerializer):
        @property
        def serializer_dataclass_field(self):
            return CustomDataclassSerializer

        # Implement additional and/or override existing methods here

.. _`PEP 591`: https://www.python.org/dev/peps/pep-0591/
.. _`PEP 585`: https://www.python.org/dev/peps/pep-0585/
.. _`PEP 604`: https://www.python.org/dev/peps/pep-0604/
.. _`property decorator`: https://docs.python.org/3/library/functions.html#property

Nesting
-------

Nested dataclasses
~~~~~~~~~~~~~~~~~~

If your dataclass has a field that also contains a dataclass instance, the ``DataclassSerializer`` will automatically
create another ``DataclassSerializer`` for that field, so that its value will be nested. This also works for dataclasses
contained in lists or dictionaries, or even several layers deep.

.. code:: Python

    @dataclass
    class House:
        address: str
        owner: Person
        residents: typing.List[Person]

    class HouseSerializer(DataclassSerializer):
        class Meta:
            dataclass = House

This will serialize as:

.. code:: Python

    >>> serializer = HouseSerializer(instance=house)
    >>> serializer.data
    {
        'address': 'Main Street 5',
        'owner': { 'name': 'Alice' }
        'residents': [
            { 'name': 'Alice', 'email': 'alice@example.org', ... },
            { 'name': 'Bob', 'email': 'bob@example.org', ... },
            { 'name': 'Charles', 'email': 'charles@example.org', ... }
        ]
    }

This does not give the ability to customize the field generation of the nested dataclasses. If that is needed, you
should declare the serializer to be used for the nested field explicitly. Alternatively, you could use the
``extra_kwargs`` option to provide arguments to fields belonging to the nested dataclasses. Consider the following:

.. code:: Python

    @dataclass
    class Transaction:
       amount: Decimal
       account_number: str

    @dataclass
    class Company:
       sales: List[Transaction]

In order to tell DRF to give 2 decimal places to the transaction account number, write the serializer as follows:

.. code:: Python

    class CompanySerializer(DataclassSerializer):
        class Meta:
            dataclass = Company

            extra_kwargs = {
                'sales': {
                    # Arguments here are for the ListField generated for the sales field on Company
                    'min_length': 1,   # requires at least 1 item to be present in the sales list
                    'child_kwargs': {
                        # Arguments here are passed to the DataclassSerializer for the Transaction dataclass
                        'extra_kwargs': {
                            # Arguments here are the extra arguments for the fields in the Transaction dataclass
                            'amount': {
                                'max_digits': 6,
                                'decimal_places': 2
                            }
                        }
                    }
                }
            }

Nesting models
~~~~~~~~~~~~~~

Likewise, if your dataclass has a field that contains a Django model, the ``DataclassSerializer`` will automatically
generate a relational field for you.

.. code:: Python

    class Company(models.Model):
        name = models.CharField()

    @dataclass
    class Person:
        name: str
        employer: Company

This will serialize as:

.. code:: Python

    >>> serializer = PersonSerializer(instance=user)
    >>> print(repr(serializer))
    PersonSerializer():
        name = fields.CharField()
        employer = fields.PrimaryKeyRelatedField(queryset=Company.objects.all())
    >>> serializer.data
    {
        "name": "Alice",
        "employer": 1
    }

If you want to nest the model in the serialized representation, you should specify the model serializer to be used by
declaring the field explicitly.

If you prefer to use hyperlinks to represent relationships rather than primary keys, in the same package you can find
the ``HyperlinkedDataclassSerializer`` class: it generates a ``HyperlinkedRelatedField`` instead of a
``PrimaryKeyRelatedField``.

New serializer field types
--------------------------
To handle some types for which DRF does not ship a serializer field, some new serializer field types are shipped in the
``rest_framework_dataclasses.fields`` namespace. These fields can be used independently of the ``DataclassSerializer``
as well.

DefaultDecimalField
~~~~~~~~~~~~~~~~~~~
A subclass of `DecimalField`_ that defaults ``max_digits`` to ``None`` and ``decimal_places`` to 2. Used to represent
decimal values which there is no explicit field configured.

EnumField
~~~~~~~~~
A subclass of `ChoiceField`_ to represent Python `enumerations`_. The enumeration members can be represented by either
their name or value. The member name is used as display name.

**Signature**: ``EnumField(enum_class, by_name=False)``

* ``enum_class``: The enumeration class.
* ``by_name``: Whether members are represented by their value (``False``) or name (``True``).

IterableField
~~~~~~~~~~~~~
A subclass of `ListField`_ that can return values that aren't of type ``list``, such as ``set``.

**Signature**: ``IterableField(container=list)``

* ``container``: The type of the returned iterable. Must have a constructor that accepts a single parameter of type
  ``list``, containing the values for the iterable.

MappingField
~~~~~~~~~~~~
A subclass of `DictField`_ that can return values that aren't of type ``dict``, such as ``collections.OrderedDict``.

**Signature**: ``MappingField(container=dict)``

* ``container``: The type of the returned mapping. Must have a constructor that accepts a single parameter of type
  ``dict``, containing the values for the mapping.

UnionField
~~~~~~~~~~
A field that can serialize and deserialize values of multiple types (i.e. values of a union type). The serialized
representation of this field includes an extra discriminator field (by default named ``type``) that indicates the actual
type of the value.

.. code:: Python

    @dataclass
    class A:
        a: str

    @dataclass
    class B:
        b: int

    @dataclass
    class Response:
        obj: A | B

    class ResponseSerializer(DataclassSerializer):
        class Meta:
            dataclass = Response

.. code:: Python

    >>> response = Response(obj=A('hello'))
    >>> serializer = ResponseSerializer(instance=response)
    >>> serializer.data
    {
        'obj': {'type': 'A', 'a': 'hello'}
    }
    >>> deserializer = ResponseSerializer(data={'obj': {'type': 'B', 'b': 42}})
    >>> deserializer.is_valid()
    True
    >>> deserializer.validated_data
    Response(obj=B(b=42))

The name of the discriminator field can be changed by setting the ``discriminator_field_name`` keyword argument for the
field:

.. code:: Python

    @dataclass
    class Response:
        obj: A | B = dataclasses.field(metadata={'serializer_kwargs': {'discriminator_field_name': 'a_or_b'}})

    # or:
    class ResponseSerializer(DataclassSerializer):
        class Meta:
            dataclass = Response
            extra_kwargs = {
                'obj': {'discriminator_field_name': 'a_or_b'}
            }

Unions containing a type that does not serialize to a mapping (e.g. an integer or string) can be serialized by enabling
nesting with the ``nest_value`` keyword argument:

.. code:: Python

    @dataclass
    class Response:
        amount: int | float

    class ResponseSerializer(DataclassSerializer):
        class Meta:
            dataclass = Response
            extra_kwargs = {
                'amount': {'nest_value': True}
            }

.. code:: Python

    >>> response = Response(amount=42)
    >>> serializer = ResponseSerializer(instance=response)
    >>> serializer.data
    {
        'amount': {'type': 'int', 'value': 42}
    }

**Signature**: ``UnionField(child_fields, nest_value=False, discriminator_field_name=None, value_field_name=None)``.

* ``child_fields``: A dictionary mapping the individual types to the serializer field to be used for them.
* ``nest_value``: Whether the value should be put under a key (``True``), or merged directly into the serialized
  representation of this field (``False``). This is disabled by default, and should usually only be set to ``True`` if
  any of the union member types is a primitive.
* ``discriminator_field_name``: Name of the discriminator field, defaults to ``type``.
* ``value_field_name``: Name of the field under which values are nested if ``nest_value`` is used defaults to ``value``.

The values used in the discriminator field can be changed by subclassing ``UnionField`` and overriding the
``get_discriminator(self, type)`` method. The lone argument to this method is one of the member types of union (a key
from the ``child_fields`` parameter), and it should return the appropriate string to be used in the discriminator field
for values of this type.

.. _`enumerations`: https://docs.python.org/3/library/enum.html
.. _`ChoiceField`: https://www.django-rest-framework.org/api-guide/fields/#choicefield
.. _`DecimalField`: https://www.django-rest-framework.org/api-guide/fields/#decimalfield
.. _`ListField`: https://www.django-rest-framework.org/api-guide/fields/#listfield
.. _`DictField`: https://www.django-rest-framework.org/api-guide/fields/#dictfield

Advanced usage
--------------

* The output of methods or properties on the dataclass can be included as a (read-only) field in the serialized state
  by adding their name to the ``fields`` option in the ``Meta`` class.

* If you don't need to customize the generated fields, ``DataclassSerializer`` can also be used directly without
  creating a subclass. In that case, the dataclass should be specified using the ``dataclass`` constructor parameter:

  .. code:: Python

    serializer = DataclassSerializer(data=request.data, dataclass=Person)

* Partial updates are supported by setting the ``partial`` argument to ``True``. Nested dataclasses will also be
  partially updated, but nested fields and dictionaries will be replaced in full with the supplied value:

  .. code:: Python

    @dataclass
    class Company:
        name: str
        location: Optional[str] = None

    @dataclass
    class Person:
        name: str
        current_employer: Company
        past_employers: List[Company]

    alice = Person(name='Alice',
                   current_employer=Company('Acme Corp.', 'New York City'),
                   past_employers=[Company('PSF', 'Delaware'), Company('Ministry of Silly Walks', 'London')])

    data = {'current_employer': {'location': 'Los Angeles'}, 'past_employers': [{'name': 'OsCorp', 'location': 'NYC'}]}

    >>> serializer = PersonSerializer(partial=True, instance=alice, data=data)
    >>> print(serializer.save())
    Person(name='Alice',
           current_employer=Company('Acme Corp.', 'Los Angeles'),
           past_employers=[Company(name='OsCorp', location='NYC')])

* If you override the ``create()`` or ``update()`` methods, the dataclass instance passed in the ``validated_data``
  argument will have the special ``rest_framework.fields.empty`` value for any fields for which no data was provided.
  This is required to distinguish between not-provided fields and fields with the default value, as needed for (both
  regular and partial) updates. You can get rid of these ``empty`` markers and replace them with the default value by
  calling the parent ``update()`` or ``create()`` methods - this is the only thing they do.

  .. code:: Python

    class CompanySerializer(DataclassSerializer):
        def create(self, validated_data):
            instance = super(CompanySerializer, self).create(validated_data)
            # if no value is provided for location, these will both hold
            assert validated_data.location == rest_framework.fields.empty
            assert instance.location is None  # None is the default value of Company.location (see previous example)

  The ``validated_data`` property on the serializer has these ``empty`` markers stripped as well, and replaced with the
  default values for not-provided fields. Note that this means you cannot access ``validated_data`` on the serializer
  for partial updates where no data has been provided for fields without a default value, an Exception will be thrown.

Schemas
-------

Starting from version 0.21.2, `drf-spectacular`_ natively supports ``DataclassSerializer``. For previous versions, you
can include the `extension`_ in your project manually. You don't need to configure it, but you do need to import the
module that contains the extension.

.. _`drf-spectacular`: https://github.com/tfranzel/drf-spectacular
.. _`extension`: https://github.com/tfranzel/drf-spectacular/blob/master/drf_spectacular/contrib/rest_framework_dataclasses.py

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "djangorestframework-dataclasses",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "",
    "author": "",
    "author_email": "Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>",
    "download_url": "https://files.pythonhosted.org/packages/6f/15/c7df45cd0eb117ab188cb6fb9423fbb5912c91420af2aa258cf8159fc103/djangorestframework-dataclasses-1.3.0.tar.gz",
    "platform": null,
    "description": "Dataclasses serializer\n======================\n\nA `dataclasses <https://docs.python.org/3/library/dataclasses.html>`__ serializer for the `Django REST Framework\n<http://www.django-rest-framework.org/>`__.\n\n.. image:: https://github.com/oxan/djangorestframework-dataclasses/workflows/CI/badge.svg\n   :target: https://github.com/oxan/djangorestframework-dataclasses/actions?query=workflow%3ACI\n.. image:: https://codecov.io/gh/oxan/djangorestframework-dataclasses/branch/master/graph/badge.svg\n   :target: https://codecov.io/gh/oxan/djangorestframework-dataclasses\n.. image:: https://badge.fury.io/py/djangorestframework-dataclasses.svg\n   :target: https://badge.fury.io/py/djangorestframework-dataclasses\n.. image:: https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=success\n   :target: https://github.com/sponsors/oxan\n\n|\n\n.. contents:: :local:\n\nRequirements\n------------\n\n* Python (3.8+)\n* Django (3.2+)\n* Django REST Framework (3.11+)\n\nThese are the supported Python and package versions. Older versions will probably work as well, but aren't tested.\n\nInstallation\n------------\n\n::\n\n    $ pip install djangorestframework-dataclasses\n\nThis package follows `semantic versioning`_. See `CHANGELOG`_ for breaking changes and new features, and `LICENSE`_ for\nthe complete license (BSD-3-clause).\n\n.. _`semantic versioning`: https://semver.org/\n.. _`CHANGELOG`: https://github.com/oxan/djangorestframework-dataclasses/blob/master/CHANGELOG.rst\n.. _`LICENSE`: https://github.com/oxan/djangorestframework-dataclasses/blob/master/LICENSE\n\nBasic usage\n-----------\n\nThe package provides the ``DataclassSerializer`` serializer, defined in the ``rest_framework_dataclasses.serializers``\nnamespace.\n\n.. code:: Python\n\n    from rest_framework_dataclasses.serializers import DataclassSerializer\n\nThis serializer provides a shortcut that lets you automatically create a ``Serializer`` class with fields that\ncorrespond to the fields on a dataclass. In usage, the ``DataclassSerializer`` is the same as a regular ``Serializer``\nclass, except that:\n\n* It will automatically generate fields for you, based on the declaration in the dataclass.\n* To make this possible it requires that a ``dataclass`` property is specified in the ``Meta`` subclass, with as value\n  a dataclass that has type annotations.\n* It includes default implementations of ``.create()`` and ``.update()``.\n\nFor example, define a dataclass as follows:\n\n.. code:: Python\n\n    @dataclass\n    class Person:\n        name: str\n        email: str\n        alive: bool\n        gender: typing.Literal['male', 'female']\n        birth_date: typing.Optional[datetime.date]\n        phone: typing.List[str]\n        movie_ratings: typing.Dict[str, int]\n\nThe serializer for this dataclass can now trivially be defined without having to duplicate all fields:\n\n.. code:: Python\n\n    class PersonSerializer(DataclassSerializer):\n        class Meta:\n            dataclass = Person\n\n    # is equivalent to\n    class PersonSerializer(Serializer):\n        name = fields.CharField()\n        email = fields.CharField()\n        alive = fields.BooleanField()\n        gender = fields.ChoiceField(choices=['male', 'female'])\n        birth_date = fields.DateField(allow_null=True)\n        phone = fields.ListField(child=fields.CharField())\n        movie_ratings = fields.DictField(child=fields.IntegerField())\n\nYou can add extra fields or override default fields by declaring them explicitly on the class, just as you would for a\nregular ``Serializer`` class. This allows to specify extra field options or change a field type.\n\n.. code:: Python\n\n    class PersonSerializer(Serializer):\n        email = fields.EmailField()\n\n        class Meta:\n            dataclass = Person\n\nDataclass serializers behave in the same way and can be used in the same places as the built-in serializers from Django\nREST Framework: you can retrieve the serialized representation using the ``.data`` property, and the deserialized\ndataclass instance using the ``.validated_data`` property. Furthermore, the ``save()`` method is implemented to create\nor update an existing dataclass instance. You can find more information on serializer usage in the\n`Django REST Framework <https://www.django-rest-framework.org/api-guide/serializers/>`__ documentation.\n\nNote that this usage pattern is very similar to that of the built-in ``ModelSerializer``. This is intentional, with the\nwhole API modelled after that of ``ModelSerializer``. Most features and behaviour known from ``ModelSerializer`` applies\nto dataclass serializers as well.\n\nField mapping\n-------------\n\nCurrently, automatic field generation is supported for the following types and their subclasses:\n\n* ``str``, ``bool``, ``int`` and ``float``.\n* ``date``, ``datetime``, ``time`` and ``timedelta`` from the ``datetime`` package.\n* ``decimal.Decimal`` (``max_digits`` and ``decimal_places`` default to ``None`` and ``2`` respectively).\n* ``uuid.UUID``\n* ``enum.Enum`` (mapped to a ``EnumField``)\n* ``typing.Iterable`` (including ``typing.List`` and `PEP 585`_-style generics such as ``list[int]``).\n* ``typing.Mapping`` (including ``typing.Dict`` and `PEP 585`_-style generics such as ``dict[str, int]``).\n* ``typing.Literal`` (mapped to a ``ChoiceField``).\n* ``typing.Union`` (mapped to a ``UnionField``, including `PEP 604`_-style unions such as ``str | int``, see\n  `UnionField`_ section below for more information).\n* ``django.db.Model``\n\nThe serializer also supports type variables that have an upper bound or are constrained.\n\nCustomize field generation\n--------------------------\n\nThe auto-generated serializer fields are configured based on type qualifiers in the dataclass (these can be mixed):\n\n* Fields with a default value (factory) are marked as optional on the serializer (``required=False``). This means that\n  these fields don't need to be supplied during deserialization.\n\n* Fields marked as nullable through ``typing.Optional``, ``typing.Union[X, None]`` or ``X | None`` (`PEP 604`_) are\n  marked as nullable on the serializer (``allow_null=True``). This means that ``None`` is accepted as a valid value\n  during deserialization.\n\n* Fields marked as final through ``typing.Final`` (as in `PEP 591`_) are marked as read-only on the serializer\n  (``read_only=True``).\n\n.. code:: Python\n\n    @dataclass\n    class Person:\n        birth_date: typing.Optional[datetime.date]\n        alive: bool = True\n        species: typing.Final[str] = 'Human'\n\n    # the autogenerated serializer will be equal to\n    class PersonSerializer(Serializer):\n        birth_date = fields.DateField(allow_null=True)\n        alive = fields.BooleanField(required=False)\n        species = fields.CharField(read_only=True)\n\nBesides overriding fields by declaring them explicitly on the serializer, you can also change or override the generated\nserializer field using metadata on the dataclass field. Currently, two keys are recognized in this dictionary:\n\n* ``serializer_field`` can be used to replace the auto-generated field with a user-supplied one. Should contain an\n  instance of a field, not a field type.\n\n* ``serializer_kwargs`` can be used to specify arbitrary additional keyword arguments for the generated field. Manually\n  specified arguments will have precedence over generated arguments (so e.g. by supplying ``{required: True}``, a field\n  with a default value can be made required).\n\n.. code:: Python\n\n    @dataclasses.dataclass\n    class Person:\n        email: str = dataclasses.field(metadata={'serializer_field': fields.EmailField()})\n        age: int = dataclasses.field(metadata={'serializer_kwargs': {'min_value': 0}})\n\n    # the autogenerated serializer will be equal to\n    class PersonSerializer(Serializer):\n        email = fields.EmailField()\n        age = fields.IntegerField(min_value=0)\n\nTo further customize the serializer, the ``DataclassSerializer`` accepts the following options in the ``Meta``\nsubclass. All options have the same behaviour as the identical options in ``ModelSerializer``.\n\n* ``dataclass`` specifies the type of dataclass used by the serializer. This is equivalent to the ``model`` option in\n  ``ModelSerializer``.\n\n* ``fields`` and ``exclude`` can be used to specify which fields should respectively be included and excluded in the\n  serializer. These cannot both be specified.\n\n  The ``fields`` option accepts the magic value ``__all__`` to specify that all fields on the dataclass should be used.\n  This is also the default value, so it is not mandatory to specify either ``fields`` or ``exclude``.\n\n* ``read_only_fields`` can be used to mark a subset of fields as read-only.\n\n* ``extra_kwargs`` can be used to specify arbitrary additional keyword arguments on fields. This can be useful to\n  extend or change the autogenerated field without explicitly declaring the field on the serializer. This option should\n  be a dictionary, mapping field names to a dictionary of keyword arguments.\n\n  If the autogenerated field is a composite field (a list or dictionary), the arguments are applied to the composite\n  field. To add keyword arguments to the composite field's child field (that is, the field used for the items in the\n  list or dictionary), they should be specified as a nested dictionary under the ``child_kwargs`` name (see\n  `Nested dataclasses`_ section below for an example).\n\n  .. code:: Python\n\n    class PersonSerializer(DataclassSerializer):\n        class Meta:\n            extra_kwargs = {\n                'height': { 'decimal_places': 1 },\n                'movie_ratings': { 'child_kwargs': { 'min_value': 0, 'max_value': 10 } }\n            }\n\n* ``validators`` functionality is unchanged.\n\n* ``depth`` (as known from ``ModelSerializer``) is not supported, it will always nest infinitely deep.\n\nChanging default behaviour\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdditionally, it is possible to change the default behaviour of the ``DataclassSerializer`` by setting one of these\nproperties on the class:\n\n* The ``serializer_field_mapping`` property contains a dictionary that maps types to REST framework serializer classes.\n  You can override or extend this mapping to change the serializer field classes that are used for fields based on\n  their type. This dictionary also accepts dataclasses as keys to change the serializer used for a nested dataclass.\n\n* The ``serializer_related_field`` property is the serializer field class that is used for relations to models.\n\n* The ``serializer_union_field`` property is the serializer field class that is used for union types.\n\n* The ``serializer_dataclass_field`` property is the serializer field class that is used for nested dataclasses. Note\n  that since Python process the class body before it defines the class, this property is implemented using the\n  `property decorator`_ to allow it to reference the containing class.\n\nFinally, you can create a subclass that overrides methods of the ``DataclassSerializer``. The field generation is\ncontrolled by the following methods, which are considered a stable part of the API:\n\n* The ``build_unknown_field()`` method is called to create serializer fields for dataclass fields that are not\n  understood. By default this just throws an error, but you can extend this with custom logic to create serializer\n  fields.\n\n* The ``build_property_field()`` method is called to create serializer fields for methods. By default this creates a\n  read-only field with the method return value.\n\n* The ``build_standard_field()``, ``build_relational_field()``, ``build_dataclass_field()``, ``build_union_field()``,\n  ``build_enum_field()``, ``build_literal_field()`` and ``build_composite_field()`` methods are used to process\n  respectively fields, nested models, nested dataclasses, union types, enums, literals, and lists or dictionaries. These\n  can be overridden to change the field generation logic.\n\nNote that when creating a subclass of ``DataclassSerializer``, most likely you will want to set the\n``serializer_dataclass_field`` property to the subclass, so that any nested dataclasses are serialized using the\nsubclass as well.\n\n.. code:: Python\n\n    class CustomDataclassSerializer(DataclassSerializer):\n        @property\n        def serializer_dataclass_field(self):\n            return CustomDataclassSerializer\n\n        # Implement additional and/or override existing methods here\n\n.. _`PEP 591`: https://www.python.org/dev/peps/pep-0591/\n.. _`PEP 585`: https://www.python.org/dev/peps/pep-0585/\n.. _`PEP 604`: https://www.python.org/dev/peps/pep-0604/\n.. _`property decorator`: https://docs.python.org/3/library/functions.html#property\n\nNesting\n-------\n\nNested dataclasses\n~~~~~~~~~~~~~~~~~~\n\nIf your dataclass has a field that also contains a dataclass instance, the ``DataclassSerializer`` will automatically\ncreate another ``DataclassSerializer`` for that field, so that its value will be nested. This also works for dataclasses\ncontained in lists or dictionaries, or even several layers deep.\n\n.. code:: Python\n\n    @dataclass\n    class House:\n        address: str\n        owner: Person\n        residents: typing.List[Person]\n\n    class HouseSerializer(DataclassSerializer):\n        class Meta:\n            dataclass = House\n\nThis will serialize as:\n\n.. code:: Python\n\n    >>> serializer = HouseSerializer(instance=house)\n    >>> serializer.data\n    {\n        'address': 'Main Street 5',\n        'owner': { 'name': 'Alice' }\n        'residents': [\n            { 'name': 'Alice', 'email': 'alice@example.org', ... },\n            { 'name': 'Bob', 'email': 'bob@example.org', ... },\n            { 'name': 'Charles', 'email': 'charles@example.org', ... }\n        ]\n    }\n\nThis does not give the ability to customize the field generation of the nested dataclasses. If that is needed, you\nshould declare the serializer to be used for the nested field explicitly. Alternatively, you could use the\n``extra_kwargs`` option to provide arguments to fields belonging to the nested dataclasses. Consider the following:\n\n.. code:: Python\n\n    @dataclass\n    class Transaction:\n       amount: Decimal\n       account_number: str\n\n    @dataclass\n    class Company:\n       sales: List[Transaction]\n\nIn order to tell DRF to give 2 decimal places to the transaction account number, write the serializer as follows:\n\n.. code:: Python\n\n    class CompanySerializer(DataclassSerializer):\n        class Meta:\n            dataclass = Company\n\n            extra_kwargs = {\n                'sales': {\n                    # Arguments here are for the ListField generated for the sales field on Company\n                    'min_length': 1,   # requires at least 1 item to be present in the sales list\n                    'child_kwargs': {\n                        # Arguments here are passed to the DataclassSerializer for the Transaction dataclass\n                        'extra_kwargs': {\n                            # Arguments here are the extra arguments for the fields in the Transaction dataclass\n                            'amount': {\n                                'max_digits': 6,\n                                'decimal_places': 2\n                            }\n                        }\n                    }\n                }\n            }\n\nNesting models\n~~~~~~~~~~~~~~\n\nLikewise, if your dataclass has a field that contains a Django model, the ``DataclassSerializer`` will automatically\ngenerate a relational field for you.\n\n.. code:: Python\n\n    class Company(models.Model):\n        name = models.CharField()\n\n    @dataclass\n    class Person:\n        name: str\n        employer: Company\n\nThis will serialize as:\n\n.. code:: Python\n\n    >>> serializer = PersonSerializer(instance=user)\n    >>> print(repr(serializer))\n    PersonSerializer():\n        name = fields.CharField()\n        employer = fields.PrimaryKeyRelatedField(queryset=Company.objects.all())\n    >>> serializer.data\n    {\n        \"name\": \"Alice\",\n        \"employer\": 1\n    }\n\nIf you want to nest the model in the serialized representation, you should specify the model serializer to be used by\ndeclaring the field explicitly.\n\nIf you prefer to use hyperlinks to represent relationships rather than primary keys, in the same package you can find\nthe ``HyperlinkedDataclassSerializer`` class: it generates a ``HyperlinkedRelatedField`` instead of a\n``PrimaryKeyRelatedField``.\n\nNew serializer field types\n--------------------------\nTo handle some types for which DRF does not ship a serializer field, some new serializer field types are shipped in the\n``rest_framework_dataclasses.fields`` namespace. These fields can be used independently of the ``DataclassSerializer``\nas well.\n\nDefaultDecimalField\n~~~~~~~~~~~~~~~~~~~\nA subclass of `DecimalField`_ that defaults ``max_digits`` to ``None`` and ``decimal_places`` to 2. Used to represent\ndecimal values which there is no explicit field configured.\n\nEnumField\n~~~~~~~~~\nA subclass of `ChoiceField`_ to represent Python `enumerations`_. The enumeration members can be represented by either\ntheir name or value. The member name is used as display name.\n\n**Signature**: ``EnumField(enum_class, by_name=False)``\n\n* ``enum_class``: The enumeration class.\n* ``by_name``: Whether members are represented by their value (``False``) or name (``True``).\n\nIterableField\n~~~~~~~~~~~~~\nA subclass of `ListField`_ that can return values that aren't of type ``list``, such as ``set``.\n\n**Signature**: ``IterableField(container=list)``\n\n* ``container``: The type of the returned iterable. Must have a constructor that accepts a single parameter of type\n  ``list``, containing the values for the iterable.\n\nMappingField\n~~~~~~~~~~~~\nA subclass of `DictField`_ that can return values that aren't of type ``dict``, such as ``collections.OrderedDict``.\n\n**Signature**: ``MappingField(container=dict)``\n\n* ``container``: The type of the returned mapping. Must have a constructor that accepts a single parameter of type\n  ``dict``, containing the values for the mapping.\n\nUnionField\n~~~~~~~~~~\nA field that can serialize and deserialize values of multiple types (i.e. values of a union type). The serialized\nrepresentation of this field includes an extra discriminator field (by default named ``type``) that indicates the actual\ntype of the value.\n\n.. code:: Python\n\n    @dataclass\n    class A:\n        a: str\n\n    @dataclass\n    class B:\n        b: int\n\n    @dataclass\n    class Response:\n        obj: A | B\n\n    class ResponseSerializer(DataclassSerializer):\n        class Meta:\n            dataclass = Response\n\n.. code:: Python\n\n    >>> response = Response(obj=A('hello'))\n    >>> serializer = ResponseSerializer(instance=response)\n    >>> serializer.data\n    {\n        'obj': {'type': 'A', 'a': 'hello'}\n    }\n    >>> deserializer = ResponseSerializer(data={'obj': {'type': 'B', 'b': 42}})\n    >>> deserializer.is_valid()\n    True\n    >>> deserializer.validated_data\n    Response(obj=B(b=42))\n\nThe name of the discriminator field can be changed by setting the ``discriminator_field_name`` keyword argument for the\nfield:\n\n.. code:: Python\n\n    @dataclass\n    class Response:\n        obj: A | B = dataclasses.field(metadata={'serializer_kwargs': {'discriminator_field_name': 'a_or_b'}})\n\n    # or:\n    class ResponseSerializer(DataclassSerializer):\n        class Meta:\n            dataclass = Response\n            extra_kwargs = {\n                'obj': {'discriminator_field_name': 'a_or_b'}\n            }\n\nUnions containing a type that does not serialize to a mapping (e.g. an integer or string) can be serialized by enabling\nnesting with the ``nest_value`` keyword argument:\n\n.. code:: Python\n\n    @dataclass\n    class Response:\n        amount: int | float\n\n    class ResponseSerializer(DataclassSerializer):\n        class Meta:\n            dataclass = Response\n            extra_kwargs = {\n                'amount': {'nest_value': True}\n            }\n\n.. code:: Python\n\n    >>> response = Response(amount=42)\n    >>> serializer = ResponseSerializer(instance=response)\n    >>> serializer.data\n    {\n        'amount': {'type': 'int', 'value': 42}\n    }\n\n**Signature**: ``UnionField(child_fields, nest_value=False, discriminator_field_name=None, value_field_name=None)``.\n\n* ``child_fields``: A dictionary mapping the individual types to the serializer field to be used for them.\n* ``nest_value``: Whether the value should be put under a key (``True``), or merged directly into the serialized\n  representation of this field (``False``). This is disabled by default, and should usually only be set to ``True`` if\n  any of the union member types is a primitive.\n* ``discriminator_field_name``: Name of the discriminator field, defaults to ``type``.\n* ``value_field_name``: Name of the field under which values are nested if ``nest_value`` is used defaults to ``value``.\n\nThe values used in the discriminator field can be changed by subclassing ``UnionField`` and overriding the\n``get_discriminator(self, type)`` method. The lone argument to this method is one of the member types of union (a key\nfrom the ``child_fields`` parameter), and it should return the appropriate string to be used in the discriminator field\nfor values of this type.\n\n.. _`enumerations`: https://docs.python.org/3/library/enum.html\n.. _`ChoiceField`: https://www.django-rest-framework.org/api-guide/fields/#choicefield\n.. _`DecimalField`: https://www.django-rest-framework.org/api-guide/fields/#decimalfield\n.. _`ListField`: https://www.django-rest-framework.org/api-guide/fields/#listfield\n.. _`DictField`: https://www.django-rest-framework.org/api-guide/fields/#dictfield\n\nAdvanced usage\n--------------\n\n* The output of methods or properties on the dataclass can be included as a (read-only) field in the serialized state\n  by adding their name to the ``fields`` option in the ``Meta`` class.\n\n* If you don't need to customize the generated fields, ``DataclassSerializer`` can also be used directly without\n  creating a subclass. In that case, the dataclass should be specified using the ``dataclass`` constructor parameter:\n\n  .. code:: Python\n\n    serializer = DataclassSerializer(data=request.data, dataclass=Person)\n\n* Partial updates are supported by setting the ``partial`` argument to ``True``. Nested dataclasses will also be\n  partially updated, but nested fields and dictionaries will be replaced in full with the supplied value:\n\n  .. code:: Python\n\n    @dataclass\n    class Company:\n        name: str\n        location: Optional[str] = None\n\n    @dataclass\n    class Person:\n        name: str\n        current_employer: Company\n        past_employers: List[Company]\n\n    alice = Person(name='Alice',\n                   current_employer=Company('Acme Corp.', 'New York City'),\n                   past_employers=[Company('PSF', 'Delaware'), Company('Ministry of Silly Walks', 'London')])\n\n    data = {'current_employer': {'location': 'Los Angeles'}, 'past_employers': [{'name': 'OsCorp', 'location': 'NYC'}]}\n\n    >>> serializer = PersonSerializer(partial=True, instance=alice, data=data)\n    >>> print(serializer.save())\n    Person(name='Alice',\n           current_employer=Company('Acme Corp.', 'Los Angeles'),\n           past_employers=[Company(name='OsCorp', location='NYC')])\n\n* If you override the ``create()`` or ``update()`` methods, the dataclass instance passed in the ``validated_data``\n  argument will have the special ``rest_framework.fields.empty`` value for any fields for which no data was provided.\n  This is required to distinguish between not-provided fields and fields with the default value, as needed for (both\n  regular and partial) updates. You can get rid of these ``empty`` markers and replace them with the default value by\n  calling the parent ``update()`` or ``create()`` methods - this is the only thing they do.\n\n  .. code:: Python\n\n    class CompanySerializer(DataclassSerializer):\n        def create(self, validated_data):\n            instance = super(CompanySerializer, self).create(validated_data)\n            # if no value is provided for location, these will both hold\n            assert validated_data.location == rest_framework.fields.empty\n            assert instance.location is None  # None is the default value of Company.location (see previous example)\n\n  The ``validated_data`` property on the serializer has these ``empty`` markers stripped as well, and replaced with the\n  default values for not-provided fields. Note that this means you cannot access ``validated_data`` on the serializer\n  for partial updates where no data has been provided for fields without a default value, an Exception will be thrown.\n\nSchemas\n-------\n\nStarting from version 0.21.2, `drf-spectacular`_ natively supports ``DataclassSerializer``. For previous versions, you\ncan include the `extension`_ in your project manually. You don't need to configure it, but you do need to import the\nmodule that contains the extension.\n\n.. _`drf-spectacular`: https://github.com/tfranzel/drf-spectacular\n.. _`extension`: https://github.com/tfranzel/drf-spectacular/blob/master/drf_spectacular/contrib/rest_framework_dataclasses.py\n",
    "bugtrack_url": null,
    "license": "Copyright (c) 2019-2021, Oxan van Leeuwen  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ",
    "summary": "A dataclasses serializer for Django REST Framework",
    "version": "1.3.0",
    "project_urls": {
        "Changelog": "https://github.com/oxan/djangorestframework-dataclasses/blob/master/CHANGELOG.rst",
        "Documentation": "https://github.com/oxan/djangorestframework-dataclasses/blob/master/README.rst",
        "Repository": "https://github.com/oxan/djangorestframework-dataclasses",
        "Sponsor": "https://github.com/sponsors/oxan"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "62ba8a6784d2a278b65cc685fc482516a5fe510555126a6b5f518407f64a20f1",
                "md5": "e6d52f294943a2408e3b9365e39b86d9",
                "sha256": "802300f812a96715079209f8dabecbab0b9716ae34a770db61f51a6897916abf"
            },
            "downloads": -1,
            "filename": "djangorestframework_dataclasses-1.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e6d52f294943a2408e3b9365e39b86d9",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 26484,
            "upload_time": "2023-08-21T20:04:46",
            "upload_time_iso_8601": "2023-08-21T20:04:46.184633Z",
            "url": "https://files.pythonhosted.org/packages/62/ba/8a6784d2a278b65cc685fc482516a5fe510555126a6b5f518407f64a20f1/djangorestframework_dataclasses-1.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6f15c7df45cd0eb117ab188cb6fb9423fbb5912c91420af2aa258cf8159fc103",
                "md5": "3092f87e948de5a37c3fad08f1d7d2fe",
                "sha256": "b061ef12f23394e91107f9b915dec38fb0a69d97a0ac9e62207aa90908375211"
            },
            "downloads": -1,
            "filename": "djangorestframework-dataclasses-1.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "3092f87e948de5a37c3fad08f1d7d2fe",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 36393,
            "upload_time": "2023-08-21T20:04:48",
            "upload_time_iso_8601": "2023-08-21T20:04:48.427681Z",
            "url": "https://files.pythonhosted.org/packages/6f/15/c7df45cd0eb117ab188cb6fb9423fbb5912c91420af2aa258cf8159fc103/djangorestframework-dataclasses-1.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-21 20:04:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "oxan",
    "github_project": "djangorestframework-dataclasses",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "djangorestframework-dataclasses"
}
        
Elapsed time: 0.29787s