strawberry-django
strawberry-django copied to clipboard
Migrating from django-graphene-cud
We are looking into moving away from graphene and strawberry looks like a very nice alternative!
Unfortunately we have some heavy integration with django-graphene-cud which offers a lot of useful features (some are too magic for my liking). I was unable to find a way to use this package in a way that offered us the hooks we needed for some of our mutations and ended up on a boilerplate heavy strawberry only PoC shown below.
Are we missing anything obvious with this library?
import strawberry
from typing import Optional
from django.db.models.query_utils import Q
from strawberry.permission import BasePermission
from strawberry.types.info import Info
from foo.graphql.types import FooNode
from foo.models import Foo
from project.graphql.context import PermissionsContext
class IsBar(BasePermission):
message = "User must be bar"
def has_permission(self, source, info: Info, **kwargs) -> bool:
return info.context.is_bar
@strawberry.interface
class FooFields:
code: Optional[str] = None
description: Optional[str] = None
@strawberry.input
class CreateFooInput(FooFields):
def save(self, info: Info):
fields = self.__dict__
fields["owner"] = info.context.user
foo = Foo.objects.create(**fields)
return foo
@strawberry.input
class PatchFooInput(FooFields):
def patch(self, info: Info, id: str):
fields = self.__dict__
permissions: PermissionsContext = info.context
filter = Q(owner=permissions.user)
if permissions.is_company_admin:
filter |= Q(owner__company_id=permissions.company_id)
queryset = Foo.objects.filter(id=id).filter(filter)
queryset.update(**fields)
return queryset.get()
@strawberry.type
class Mutation:
@strawberry.field(permission_classes=[IsEmployed])
def create_foo(self, info: Info, input: CreateFooInput) -> FooNode:
return input.save(info)
@strawberry.field(permission_classes=[IsEmployed])
def update_foo(self, info: Info, id: str, input: PatchFooInput) -> FooNode:
return input.patch(info, id)
Upvote & Fund
- We're using Polar.sh so you can upvote and help fund this issue.
- We receive the funding once the issue is completed & confirmed by you.
- Thank you in advance for helping prioritize & fund our backlog.
Do you have any specific questions you would like to get answer? Your example code looks quite generic, which means that you probably want to ask some of your questions in strawberry core project: https://github.com/strawberry-graphql/strawberry
It was suggested I ask on here when I asked on discord.
My question is, how can I hook into the mutations.*
utilities so that I can add business logic, rather than having to reimplement them with my custom logic. For example, when creating a Foo
I might want to populate an owner
field based on the request context. Or If I am updating Bar
I might want to check user has the correct permissions or perform certain business logic dependent on state.
You can extend existing mutation classes and add your business logic there. Unfortunately I cannot verify the example code just now but you get the idea
from strawberry_django.mutations.fields import DjangoUpdateMutation
class YourUpdateMutation(DjangoUpdateMutation):
def resolver(self, info, source, data, **kwargs):
queryset = super().resolver(info, source, data, **kwargs)
queryset.update(owner=...)
return queryset
@strawberry.type
class Mutation:
update_foo: List[FooNode] = YourUpdateMutation(PatchFooInput)
I too have heavy usage of graphene-django-cud and find it to be critical for reducing a ton of boilerplate, so would not be able to migrate from graphene to strawberry without some equivalent.
I recently came across strawberry-django-extras which seems to support nested mutations and presumably help with DRYing up mutation code. Haven't used it myself yet but looks promising!