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

Potential fix for #647 #649

Merged
merged 5 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion django_unicorn/management/commands/startunicorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def get_app_path(app_name: str) -> Path:


class Command(BaseCommand):
help = "Creates a new component for `django-unicorn`" # noqa: A003
help = "Creates a new component for `django-unicorn`"

def add_arguments(self, parser):
parser.add_argument("app_name", type=str)
Expand Down
16 changes: 16 additions & 0 deletions django_unicorn/typer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@
from typing import get_type_hints as typing_get_type_hints
from uuid import UUID

try:
from pydantic import BaseModel

def _check_pydantic(cls) -> bool:
return issubclass(cls, BaseModel)

except ImportError:

def _check_pydantic(cls) -> bool: # noqa: ARG001
return False


from django.db.models import Model, QuerySet
from django.utils.dateparse import (
parse_date,
Expand Down Expand Up @@ -140,6 +152,10 @@ def cast_value(type_hint, value):
if issubclass(type_hint, Model):
continue

if _check_pydantic(type_hint) or is_dataclass(type_hint):
value = type_hint(**value)
break

value = type_hint(value)
break

Expand Down
3 changes: 1 addition & 2 deletions tests/serializer/test_dumps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pytest
from django.db import models
from django.utils.timezone import now
from pydantic import BaseModel

from django_unicorn import serializer
from django_unicorn.serializer import InvalidFieldAttributeError, InvalidFieldNameError
Expand Down Expand Up @@ -698,8 +699,6 @@ def test_nested_list_float_less_complicated():


def test_pydantic():
from pydantic import BaseModel

class Book(BaseModel):
title = "The Grapes of Wrath"
author = "John Steinbeck"
Expand Down
61 changes: 60 additions & 1 deletion tests/test_typer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import datetime
from typing import Optional
from dataclasses import dataclass
from typing import List, Optional
from typing import get_type_hints as typing_get_type_hints

from pydantic import BaseModel

from django_unicorn.components import UnicornView
from django_unicorn.typer import cast_attribute_value, cast_value, get_type_hints
from example.coffee.models import Flavor
Expand Down Expand Up @@ -111,3 +114,59 @@ def test_cast_value_model_int():
actual = cast_value(type_hint, 1)

assert actual == 1


@dataclass
class DataClass:
name: str


class PydanticBaseModel(BaseModel):
name: str


class AnotherExampleClass:
data: DataClass
list_data: List[DataClass]
pydantic_data: PydanticBaseModel
pydantic_list_data: List[PydanticBaseModel]


def test_cast_value_dataclass():
example_class = AnotherExampleClass()
test_data = DataClass(name="foo")
example_class.data = test_data
type_hints = typing_get_type_hints(example_class)
type_hint = type_hints["data"]
actual = cast_value(type_hint, {"name": "foo"})
assert actual == test_data


def test_cast_value_pydantic():
example_class = AnotherExampleClass()
test_data = PydanticBaseModel(name="foo")
example_class.pydantic_data = test_data
type_hints = typing_get_type_hints(example_class)
type_hint = type_hints["pydantic_data"]
actual = cast_value(type_hint, {"name": "foo"})
assert actual == test_data


def test_cast_value_list_dataclass():
example_class = AnotherExampleClass()
test_data = DataClass(name="foo")
example_class.pydantic_list_data = [test_data]
type_hints = typing_get_type_hints(example_class)
type_hint = type_hints["list_data"]
actual = cast_value(type_hint, [{"name": "foo"}])
assert actual == [test_data]


def test_cast_value_list_pydantic():
example_class = AnotherExampleClass()
test_data = PydanticBaseModel(name="foo")
example_class.pydantic_list_data = [test_data]
type_hints = typing_get_type_hints(example_class)
type_hint = type_hints["pydantic_list_data"]
actual = cast_value(type_hint, [{"name": "foo"}])
assert actual == [test_data]
2 changes: 1 addition & 1 deletion tests/views/fake_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class FakeModelFormComponent(UnicornView):

title = None
date_published = None
type = None # noqa: A003
type = None


class FakeModelComponent(UnicornView):
Expand Down