Skip to content

Commit

Permalink
Add django-jsonform widget injection for v2 implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
surenkov committed Mar 31, 2024
1 parent 6e9dcc3 commit 32445da
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: |
python -m pip install -e .[dev,test]
- name: Lint package
run: mypy .
run: python -m mypy .

pre-commit:
runs-on: ubuntu-latest
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
DJANGO_SETTINGS_MODULE ?= "tests.settings.django_test_settings"

.PHONY: install build test lint upload upload-test clean

install:
Expand All @@ -8,7 +10,11 @@ build:
python3 -m build

migrations:
DJANGO_SETTINGS_MODULE="tests.settings.django_test_settings" python3 -m django makemigrations --noinput
python3 -m django makemigrations --noinput

runserver:
python3 -m django migrate && \
python3 -m django runserver

test: A=
test:
Expand Down
15 changes: 12 additions & 3 deletions django_pydantic_field/v2/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pydantic
import typing_extensions as te
from django.apps import apps
from django.core import checks, exceptions
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.expressions import BaseExpression, Col, Value
Expand Down Expand Up @@ -70,7 +71,7 @@ class PydanticSchemaField(JSONField, ty.Generic[types.ST]):
def __init__(
self,
*args,
schema: type[types.ST] | BaseContainer | ty.ForwardRef | str | None = None,
schema: type[types.ST] | te.Annotated[type[types.ST], ...] | BaseContainer | ty.ForwardRef | str | None = None,
config: pydantic.ConfigDict | None = None,
**kwargs,
):
Expand Down Expand Up @@ -178,8 +179,16 @@ def get_default(self) -> ty.Any:
return default_value

def formfield(self, **kwargs):
field_kwargs = dict(
form_class=forms.SchemaField,
try:
if apps.is_installed("django_jsonform"):
form_cls = forms.JSONFormSchemaField
else:
form_cls = forms.SchemaField
except AttributeError:
form_cls = forms.SchemaField

field_kwargs: dict[str, ty.Any] = dict(
form_class=form_cls,
# Trying to resolve the schema before passing it to the formfield, since in Django < 4.0,
# formfield is unbound during form validation and is not able to resolve forward refs defined in the model.
schema=self.adapter.prepared_schema,
Expand Down
35 changes: 35 additions & 0 deletions django_pydantic_field/v2/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import typing as ty

import pydantic
import typing_extensions as te
from django.core.exceptions import ValidationError
from django.forms.fields import InvalidJSONInput, JSONField, JSONString
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -92,3 +93,37 @@ def _try_coerce(self, value):
value = self.adapter.validate_json(value)

return value


try:
from django_jsonform.widgets import JSONFormWidget as _JSONFormWidget
except ImportError:
pass
else:

class JSONFormSchemaWidget(_JSONFormWidget):
def __init__(
self,
schema: type[types.ST] | ty.ForwardRef | te.Annotated[type[types.ST], ...] | str,
config: pydantic.ConfigDict | None = None,
allow_null: bool | None = None,
export_kwargs: types.ExportKwargs | None = None,
**kwargs,
):
if export_kwargs is None:
export_kwargs = {}
self.adapter = types.SchemaAdapter(schema, config, None, None, allow_null, **export_kwargs)
super().__init__(self.adapter.json_schema(), **kwargs)

class JSONFormSchemaField(SchemaField):
def __init__(
self,
schema: type[types.ST] | ty.ForwardRef | te.Annotated[type[types.ST], ...] | str,
config: pydantic.ConfigDict | None = None,
allow_null: bool | None = None,
*args,
**kwargs,
):
export_kwargs = types.SchemaAdapter.extract_export_kwargs(kwargs)
kwargs.setdefault("widget", JSONFormSchemaWidget(schema, config, allow_null, export_kwargs))
super().__init__(schema, config, allow_null, *args, **kwargs)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ dependencies = [
[project.optional-dependencies]
openapi = ["uritemplate", "inflection"]
coreapi = ["coreapi"]
jsonform = ["django_jsonform>=2.0,<3"]
dev = [
"build",
"black",
Expand All @@ -63,7 +64,7 @@ dev = [
"pytest-django>=4.5,<5",
]
test = [
"django_pydantic_field[openapi,coreapi]",
"django_pydantic_field[openapi,coreapi,jsonform]",
"dj-database-url~=2.0",
"djangorestframework>=3,<4",
"pyyaml",
Expand Down
5 changes: 4 additions & 1 deletion tests/settings/django_test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
SITE_ID = 1
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_URL = "/static/"
DEBUG = True

INSTALLED_APPS = [
"django.contrib.contenttypes",
Expand All @@ -14,14 +15,15 @@
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.admin",
"django_jsonform",
"tests.sample_app",
"tests.test_app",
]

MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
]
TEMPLATES = [
{
Expand Down Expand Up @@ -56,3 +58,4 @@
CURRENT_TEST_DB = "default"

REST_FRAMEWORK = {"COMPACT_JSON": True}
ROOT_URLCONF = "tests.settings.urls"
6 changes: 6 additions & 0 deletions tests/settings/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path
from django.contrib import admin

urlpatterns = [
path("admin/", admin.site.urls),
]

0 comments on commit 32445da

Please sign in to comment.