Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update pydantic model Config classes #945

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 2 additions & 4 deletions ninja/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.conf import settings as django_settings
from pydantic import BaseModel, Field
from pydantic import ConfigDict, BaseModel, Field


class Settings(BaseModel):
Expand All @@ -19,9 +19,7 @@ class Settings(BaseModel):
"ninja.pagination.LimitOffsetPagination", alias="NINJA_PAGINATION_CLASS"
)
PAGINATION_PER_PAGE: int = Field(100, alias="NINJA_PAGINATION_PER_PAGE")

class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)


settings = Settings.model_validate(django_settings)
Expand Down
28 changes: 10 additions & 18 deletions ninja/filter_schema.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,30 @@
from typing import Any, cast
from typing import Any

from django.core.exceptions import ImproperlyConfigured
from django.db.models import Q, QuerySet
from pydantic import ConfigDict
from pydantic.fields import FieldInfo
from typing_extensions import Literal

from .schema import Schema

DEFAULT_IGNORE_NONE = True
DEFAULT_CLASS_LEVEL_EXPRESSION_CONNECTOR = "AND"
DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR = "OR"

# XOR is available only in Django 4.1+: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#xor
ExpressionConnector = Literal["AND", "OR", "XOR"]


# class FilterConfig(BaseConfig):
# ignore_none: bool = DEFAULT_IGNORE_NONE
# expression_connector: ExpressionConnector = cast(
# ExpressionConnector, DEFAULT_CLASS_LEVEL_EXPRESSION_CONNECTOR
# )
DEFAULT_IGNORE_NONE = True
DEFAULT_CLASS_LEVEL_EXPRESSION_CONNECTOR: ExpressionConnector = "AND"
DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR: ExpressionConnector = "OR"


class FilterSchema(Schema):
# if TYPE_CHECKING:
# __config__: ClassVar[Type[FilterConfig]] = FilterConfig # pragma: no cover

# Config = FilterConfig

class Config(Schema.Config):
ignore_none: bool = DEFAULT_IGNORE_NONE
expression_connector: ExpressionConnector = cast(
ExpressionConnector, DEFAULT_CLASS_LEVEL_EXPRESSION_CONNECTOR
)
model_config = ConfigDict(
**Schema.model_config,
ignore_none=DEFAULT_IGNORE_NONE,
expression_connector=DEFAULT_CLASS_LEVEL_EXPRESSION_CONNECTOR,
)

def custom_expression(self) -> Q:
"""
Expand Down
5 changes: 2 additions & 3 deletions ninja/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def resolve_name(obj):
from django.db.models import Manager, QuerySet
from django.db.models.fields.files import FieldFile
from django.template import Variable, VariableDoesNotExist
from pydantic import BaseModel, Field, ValidationInfo, model_validator, validator
from pydantic import ConfigDict, BaseModel, Field, ValidationInfo, model_validator, validator
from pydantic._internal._model_construction import ModelMetaclass
from pydantic.functional_validators import ModelWrapValidatorHandler
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
Expand Down Expand Up @@ -193,8 +193,7 @@ def default_schema(self, schema: Any) -> JsonSchemaValue:


class Schema(BaseModel, metaclass=ResolverMetaclass):
class Config:
from_attributes = True # aka orm_mode
model_config = ConfigDict(from_attributes=True)

@model_validator(mode="wrap")
@classmethod
Expand Down
12 changes: 8 additions & 4 deletions tests/demo_project/multi_param/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Test App to use with Swagger UI and in unit tests"""

from pydantic import ConfigDict

from ninja import (
Body,
Cookie,
Expand Down Expand Up @@ -44,15 +46,17 @@ class TestData(Schema):


class ResponseData(Schema):
model_config = ConfigDict(
**Schema.model_config,
alias_generator=to_kebab,
populate_by_name=True,
)

i: int
s: str
data: TestData4
nested_data: TestData

class Config(Schema.Config):
alias_generator = to_kebab
populate_by_name = True


test_data4_extra = dict(title="Data4 Title", description="Data4 Desc")

Expand Down
11 changes: 7 additions & 4 deletions tests/test_openapi_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest
from django.contrib.admin.views.decorators import staff_member_required
from django.test import Client, override_settings
from pydantic import ConfigDict

from ninja import Body, Field, File, Form, NinjaAPI, Query, Schema, UploadedFile
from ninja.openapi.urls import get_openapi_urls
Expand Down Expand Up @@ -32,13 +33,15 @@ def to_camel(string: str) -> str:


class Response(Schema):
model_config = ConfigDict(
**Schema.model_config,
alias_generator=to_camel,
populate_by_name=True,
)

i: int
f: float = Field(..., title="f title", description="f desc")

class Config(Schema.Config):
alias_generator = to_camel
populate_by_name = True


@api.post("/test", response=Response)
def method(request, data: Payload):
Expand Down
4 changes: 2 additions & 2 deletions tests/test_orm_metaclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class Meta:
app_label = "tests"

class SampleSchema(ModelSchema):
class Config:
class Meta:
model = User
model_fields = ["firstname", "lastname"]
fields = ["firstname", "lastname"]

def hello(self):
return f"Hello({self.firstname})"
Expand Down