Skip to content

Commit

Permalink
Unwrap django lazy objects in mutation resolvers (#338)
Browse files Browse the repository at this point in the history
Co-authored-by: Thiago Bellini Ribeiro <[email protected]>
  • Loading branch information
ryanprobus and bellini666 authored Aug 31, 2023
1 parent db978aa commit 79defa0
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 14 deletions.
29 changes: 20 additions & 9 deletions strawberry_django/mutations/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
ManyToOneRel,
OneToOneRel,
)
from django.utils.functional import LazyObject
from strawberry import UNSET, relay
from typing_extensions import Literal, TypeAlias, TypedDict

Expand Down Expand Up @@ -214,9 +215,9 @@ def create(

@transaction.atomic
def create(
info,
model,
data,
info: Info,
model: type[_M],
data: dict[str, Any] | list[dict[str, Any]],
*,
full_clean: bool | FullCleanOptions = True,
pre_save_hook: Callable[[_M], None] | None = None,
Expand Down Expand Up @@ -262,13 +263,18 @@ def update(

@transaction.atomic
def update(
info,
instance,
data,
info: Info,
instance: _M | Iterable[_M],
data: dict[str, Any],
*,
full_clean: bool | FullCleanOptions = True,
pre_save_hook: Callable[[_M], None] | None = None,
):
) -> _M | list[_M]:
# Unwrap lazy objects since they have a proxy __iter__ method that will make
# them iterables even if the wrapped object isn't
if isinstance(instance, LazyObject):
instance = cast(_M, instance.__reduce__()[1][0])

if isinstance(instance, Iterable):
many = True
instances = list(instance)
Expand Down Expand Up @@ -346,7 +352,7 @@ def update(

full_clean_options = full_clean if isinstance(full_clean, dict) else {}
if full_clean:
instance.full_clean(**full_clean_options)
instance.full_clean(**full_clean_options) # type: ignore

instance.save()

Expand Down Expand Up @@ -379,7 +385,12 @@ def delete(


@transaction.atomic
def delete(info, instance, *, data=None):
def delete(info: Info, instance: _M | Iterable[_M], *, data=None) -> _M | list[_M]:
# Unwrap lazy objects since they have a proxy __iter__ method that will make
# them iterables even if the wrapped object isn't
if isinstance(instance, LazyObject):
instance = cast(_M, instance.__reduce__()[1][0])

if isinstance(instance, Iterable):
many = True
instances = list(instance)
Expand Down
27 changes: 27 additions & 0 deletions tests/mutations/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
import strawberry
from django.conf import settings
from django.utils.functional import SimpleLazyObject
from strawberry import auto

import strawberry_django
Expand Down Expand Up @@ -35,6 +36,27 @@ class Mutation:
FruitPartialInput,
filters=FruitFilter,
)

@strawberry_django.mutation
def update_lazy_fruit(self, info, data: FruitPartialInput) -> Fruit:
fruit = SimpleLazyObject(lambda: models.Fruit.objects.get())
return mutations.resolvers.update(
info,
fruit,
mutations.resolvers.parse_input(
info,
vars(data),
),
)

@strawberry_django.mutation
def delete_lazy_fruit(self, info) -> Fruit:
fruit = SimpleLazyObject(lambda: models.Fruit.objects.get())
return mutations.resolvers.delete(
info,
fruit,
)

delete_fruits: List[Fruit] = mutations.delete(filters=FruitFilter)

create_color: Color = mutations.create(ColorInput)
Expand Down Expand Up @@ -64,3 +86,8 @@ class GeoMutation(Mutation):
mutation = Mutation

return utils.generate_query(mutation=mutation)


@pytest.fixture()
def fruit(db):
return models.Fruit.objects.create(name="Strawberry")
15 changes: 15 additions & 0 deletions tests/mutations/test_mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ def test_update(mutation, fruits):
]


def test_update_lazy_object(mutation, fruit):
result = mutation(
'{ fruit: updateLazyFruit(data: { name: "orange" }) { id name } }',
)
assert not result.errors
assert result.data["fruit"] == {"id": "1", "name": "orange"}


def test_update_with_filters(mutation, fruits):
result = mutation(
'{ fruits: updateFruits(data: { name: "orange" },'
Expand Down Expand Up @@ -85,6 +93,13 @@ def test_delete(mutation, fruits):
assert list(models.Fruit.objects.values("id", "name")) == []


def test_delete_lazy_object(mutation, fruit):
result = mutation("{ fruit: deleteLazyFruit { id name } }")
assert not result.errors
assert result.data["fruit"] == {"id": "1", "name": "Strawberry"}
assert list(models.Fruit.objects.values("id", "name")) == []


def test_delete_with_filters(mutation, fruits):
result = mutation(
'{ fruits: deleteFruits(filters: { name: { contains: "berry" } }) { id'
Expand Down
5 changes: 0 additions & 5 deletions tests/mutations/test_relationship.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
from tests import models


@pytest.fixture()
def fruit(db):
return models.Fruit.objects.create(name="Strawberry")


@pytest.fixture()
def color(db):
return models.Color.objects.create(name="red")
Expand Down

0 comments on commit 79defa0

Please sign in to comment.