Skip to content

Commit

Permalink
fix: fix files not being saved on create mutation (#464)
Browse files Browse the repository at this point in the history
Fix #461
  • Loading branch information
bellini666 authored Jan 27, 2024
1 parent 31a7c2f commit e751a2d
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
.tmp_upload/

# Translations
*.mo
Expand Down
87 changes: 86 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Pygments = "^2.15.1"
factory-boy = "^3.2.1"
django-guardian = "^2.4.0"
channels = { version = ">=3.0.5" }
pillow = "^10.2.0"

[tool.poetry.extras]
debug-toolbar = ["django-debug-toolbar"]
Expand Down
6 changes: 4 additions & 2 deletions strawberry_django/mutations/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,10 @@ def create(

# Now that the instance has been created, go and assign
# files and many2many fields.
for file_field, value in files:
file_field.save_form_data(instance, value)
if files:
for file_field, value in files:
file_field.save_form_data(instance, value)
instance.save()

for field, value in m2m:
update_m2m(info, instance, field, value, key_attr)
Expand Down
13 changes: 13 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import contextlib
import pathlib
import shutil
from typing import Dict, List, Tuple, Type, Union, cast

import pytest
Expand All @@ -15,6 +17,17 @@

from . import models, types, utils

_TESTS_DIR = pathlib.Path(__file__).parent
_ROOT_DIR = _TESTS_DIR.parent


@pytest.fixture(scope="session", autouse=True)
def _cleanup(request):
def cleanup_function():
shutil.rmtree(_ROOT_DIR / ".tmp_upload", ignore_errors=True)

request.addfinalizer(cleanup_function) # noqa: PT021


@pytest.fixture(params=["sync", "async", "sync_no_optimizer", "async_no_optimizer"])
def gql_client(request):
Expand Down
6 changes: 6 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class Fruit(models.Model):
default=5,
help_text="Level of sweetness, from 1 to 10",
)
picture = models.ImageField(
null=True,
blank=True,
default=None,
upload_to=".tmp_upload",
)

def name_upper(self):
return self.name.upper()
Expand Down
33 changes: 33 additions & 0 deletions tests/mutations/test_mutations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import io

import pytest
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from PIL import Image

from tests import models
from tests.utils import deep_tuple_to_list
Expand All @@ -16,6 +20,35 @@ def test_create(mutation):
]


def test_create_with_file(mutation):
img_f = io.BytesIO()
img = Image.new(mode="RGB", size=(1, 1), color="red")
img.save(img_f, format="jpeg")
upload = SimpleUploadedFile("test-picture.png", img_f.getvalue())

result = mutation(
"""\
CreateFruit($picture: Upload!) {
createFruit(data: { name: "strawberry", picture: $picture }) {
id
name
picture {
name
}
}
}
""",
variable_values={"picture": upload},
)

assert not result.errors
assert result.data["createFruit"] == {
"id": "1",
"name": "strawberry",
"picture": {"name": ".tmp_upload/test-picture.png"},
}


@pytest.mark.asyncio()
@pytest.mark.django_db(transaction=True)
async def test_create_async(mutation):
Expand Down
68 changes: 64 additions & 4 deletions tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,25 +222,35 @@ class Query:
fruit: FruitType

schema = strawberry.Schema(query=Query)
expected = """\
expected = '''\
type DjangoImageType {
name: String!
path: String!
size: Int!
url: String!
width: Int!
height: Int!
}
type DjangoModelType {
pk: ID!
}
\"\"\"Fruit(id, name, color, sweetness)\"\"\"
"""Fruit(id, name, color, sweetness, picture)"""
type FruitType {
id: ID!
name: String!
color: DjangoModelType
\"\"\"Level of sweetness, from 1 to 10\"\"\"
"""Level of sweetness, from 1 to 10"""
sweetness: Int!
picture: DjangoImageType
}
type Query {
fruit: FruitType!
}
"""
'''

assert textwrap.dedent(str(schema)) == textwrap.dedent(expected).strip()

Expand All @@ -256,6 +266,15 @@ class Query:

schema = strawberry.Schema(query=Query)
expected = """\
type DjangoImageType {
name: String!
path: String!
size: Int!
url: String!
width: Int!
height: Int!
}
type DjangoModelType {
pk: ID!
}
Expand All @@ -265,6 +284,7 @@ class Query:
id: ID!
color: DjangoModelType
sweetness: Int!
picture: DjangoImageType
}
type Query {
Expand Down Expand Up @@ -388,6 +408,15 @@ class Query:

schema = strawberry.Schema(query=Query)
expected = """\
type DjangoImageType {
name: String!
path: String!
size: Int!
url: String!
width: Int!
height: Int!
}
type DjangoModelType {
pk: ID!
}
Expand All @@ -396,6 +425,7 @@ class Query:
id: ID!
color: DjangoModelType
sweetness: Int!
picture: DjangoImageType
}
type Query {
Expand All @@ -417,6 +447,15 @@ class Query:

schema = strawberry.Schema(query=Query)
expected = """\
type DjangoImageType {
name: String!
path: String!
size: Int!
url: String!
width: Int!
height: Int!
}
type DjangoModelType {
pk: ID!
}
Expand All @@ -426,6 +465,7 @@ class Query:
name: String!
color: DjangoModelType
sweetness: Int!
picture: DjangoImageType
}
type Query {
Expand All @@ -447,6 +487,15 @@ class Query:

schema = strawberry.Schema(query=Query)
expected = """\
type DjangoImageType {
name: String!
path: String!
size: Int!
url: String!
width: Int!
height: Int!
}
type DjangoModelType {
pk: ID!
}
Expand All @@ -455,6 +504,7 @@ class Query:
sweetness: String!
id: ID!
color: DjangoModelType
picture: DjangoImageType
}
type Query {
Expand All @@ -476,6 +526,15 @@ class Query:

schema = strawberry.Schema(query=Query)
expected = """\
type DjangoImageType {
name: String!
path: String!
size: Int!
url: String!
width: Int!
height: Int!
}
type DjangoModelType {
pk: ID!
}
Expand All @@ -485,6 +544,7 @@ class Query:
id: ID!
color: DjangoModelType
sweetness: Int!
picture: DjangoImageType
}
type Query {
Expand Down
1 change: 1 addition & 0 deletions tests/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Fruit:
name: auto
color: Color | None
types: List[FruitType] # noqa: UP006
picture: auto


@strawberry_django.type(models.Color)
Expand Down

0 comments on commit e751a2d

Please sign in to comment.