django-pydantic-field

Django JSONField with Pydantic models as a Schema

OTHER License

Downloads
50.3K
Stars
111
Committers
1

Bot releases are hidden (Show)

django-pydantic-field - v0.3.0a2: Tweaks on package discovery for Pydantic v2

Published by surenkov 11 months ago

Previous pre-release contained a broken package. Here's the new one (:

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.3.0a1...v0.3.0a2

django-pydantic-field - v0.3.0a1: Pydantic v2 initial support

Published by surenkov 11 months ago

Pydantic v2 initial support

Important note: 0.3.0a1 contained a broken package. Please take a look on 0.3.0a2 if you like to test it in the wild (:

This release is specifically focused on adding v2 primitives. Pydantic v1 was supported by a trickery around passed (or annotated) schema, wrapping it into intermediary RootModel to support both pydantic models and most of arbitrary annotations.

With v2, pydantic now contains the primitive -- TypeAdapter -- which is well designed for this particular machinery.

There's one remaining primitive is to be migrated prior final 0.3.0 -- OpenAPI AutoSchema generator, but since this library is already not super-sticky to the OpenAPI standard, I decided that alpha release could be rolled out without it.

Compatibility layer

To support both Pydantic v1 and v2, which might be useful during migration step, I added an indirection level during the import step.
0.3.0 contains implementations for both versions, with changes in v1 only required to make this compatibility layer to operate, with all behaviour remaining as in 0.2.11.

Lazy schema evaluation

In 0.2.* versions, exact schema resolution had been done in three stages:

  1. First, the field tried to resolve the passed schema during field descriptor creation.
  2. If it's failed, the next attempt had been performed during Django model class preparation, particularly in contribute_to_class method.
  3. If even step 2 had failed (because of forward references might be still unresolvable at this stage), then the schema evaluation had to be postponed until the first attempt to access the field on model instance (either instance creation, field assignment or so).

In this update, I decided that evaluation logic should be as simple as possible, thus keeping only stage 3, and removing all the dirty logic that supported the machinery around 1 and 2 stages.
To mitigate possible schema evaluation errors, which may appear only in runtime, the field now performs a few checks that are being performed by Django during app development lifecycle:

  • manage.py check
  • manage.py runserver
  • manage.py makemigrations
  • manage.py migrate
  • and so on...

This check is relied on the same mechanics that you might see in plain JSONField, complaining on passing mutable structures itself, instead of callables (Django's field.E010 warning).

Additional integrity checks

Along with the schema evaluation check, the field now performes a few others, to make sure of its integrity:

  • pydantic.E001 (a check from the section above). The schema cannot be resolved. It is most likely a programmatic error -- forward references cannot be resolved in the Django model's execution context.
  • pydantic.E002. The field's default= value cannot be serialized by the specified schema. This may lead to runtime errors during model .save() or .objects.create(...) methods.
  • pydantic.E003. If the field contains include= or exclude= export arguments, there could be a situation that value, written in the database, could not be restored back to python from its serialized form. This check tries to pass the specified default value (or the one that could be inferred directly from the schema, if default= is missing), through the whole transformation cycle, yielding a warning if the value could not be transformed back to python.

Additionally, JSONField's field.E010 warning is suppressed, as it is meaningless due to the nature of field transformations -- we're always getting a new value, not reusing the one passed to default= argument.

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.11...v0.3.0a1

django-pydantic-field - v0.2.11

Published by surenkov about 1 year ago

What's Changed

New Contributors

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.10...v0.2.11

django-pydantic-field - v0.2.10

Published by surenkov about 1 year ago

What's Changed

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.9...v0.2.10

django-pydantic-field - v0.2.9

Published by surenkov about 1 year ago

What's Changed

New Contributors

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.8...v0.2.9

django-pydantic-field - v0.2.8

Published by surenkov about 1 year ago

What's Changed

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.7...v0.2.8

django-pydantic-field - v0.2.7

Published by surenkov over 1 year ago

What's Changed

New Contributors

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.6...v0.2.7

django-pydantic-field - v0.2.6

Published by surenkov over 1 year ago

What's Changed

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.5...v0.2.6

django-pydantic-field - v0.2.5

Published by surenkov over 1 year ago

What's Changed

Important note:

This release fixes #19, thus Django < 4.2 users should avoid installing previous one, v0.2.4.
I'm considering to delist 0.2.4 from PyPI to minimize the potential harm.

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.4...v0.2.5

django-pydantic-field - v0.2.4

Published by surenkov over 1 year ago

django-pydantic-field - v0.2.3

Published by surenkov over 1 year ago

What's Changed

Closed: https://github.com/surenkov/django-pydantic-field/issues/12

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.2...v0.2.3

django-pydantic-field - v0.2.2

Published by surenkov almost 2 years ago

django-pydantic-field - Properly declare supported Django versions

Published by surenkov about 2 years ago

The package supports Django starting from 3.1.

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.2.0...v0.2.1

django-pydantic-field - v0.2.0: Django 3.2 support

Published by surenkov about 2 years ago

django-pydantic-field - v0.1.13: Django form field support

Published by surenkov about 2 years ago

django-pydantic-field - v0.1.12: Union types support

Published by surenkov about 2 years ago

Un additin to typed collections and pydantic types, the field now also supports typing.Union, its special forms like typing.Optional and (only for py 3.10+) X | Y union annotations syntax.

What's Changed

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.1.11...v0.1.12

django-pydantic-field - Enfoced null checks for `SchemaField`

Published by surenkov about 2 years ago

Enforced null checks for SchemaField

In the example below, typing linters should enforce type compatibility:

class BuildingMeta(pydantic.BaseModel):
    type: Optional[BuildingTypes]

class Building(models.Model):
    opt_meta: BuildingMeta = SchemaField(default={"type": "frame"}, null=True)
    meta: Optional[BuildingMeta] = SchemaField(default={"type": "frame"})

Pyright will complain on both fields:

sample_app/models.py:5:32 - error: Expression of type "BuildingMeta | None" cannot be assigned to declared type "BuildingMeta"
sample_app/models.py:6:40 - error: Expression of type "ST@SchemaField" cannot be assigned to declared type "BuildingMeta | None"

Mypy has more broaden resolution for latter check, but still be able to recognise first one:

sample_app/models.py:5: error: Incompatible types in assignment (expression has type "Optional[BuildingMeta]", variable has type "BuildingMeta")

Fixing field annotations will resolve issues on both checkers:

class Building(models.Model):
    opt_meta: Optional[BuildingMeta] = SchemaField(default={"type": "frame"}, null=True)
    meta: BuildingMeta = SchemaField(default={"type": "frame"})

The check also enforces default=None to have null=True param.

Relaxed restrictions on default= argument

In addition to null enforcement, typing checks allow arbitrary values for default= argument, as long as they are acceptable for pydantic's BaseModel.parse_obj method. Callables are also accepted, mimicking Django's field semantics.

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.1.10...v0.1.11

django-pydantic-field - v0.1.10

Published by surenkov about 2 years ago

Slightly improve inheritance chain, better typings for SchemaField factory function.

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.1.9...v0.1.10

django-pydantic-field - Support for deferred type annotations

Published by surenkov about 2 years ago

What's Changed

Full Changelog: https://github.com/surenkov/django-pydantic-field/compare/v0.1.8...v0.1.9

django-pydantic-field - Raise correct exception type on model field validation

Published by surenkov about 2 years ago

Django model fields are required to throw django.core.exceptions.ValidationError during .to_python(value) call.
Let's stick to that recommendation.

Convert the input value into the expected Python data type, raising
django.core.exceptions.ValidationError if the data can't be converted.

Package Rankings
Top 11.13% on Pypi.org
Badges
Extracted from project README
PyPI Version Lint and Test Package PyPI - Downloads Supported Python Versions Supported Django Versions