strawberry-django icon indicating copy to clipboard operation
strawberry-django copied to clipboard

Migrating from django-graphene-cud

Open ghost opened this issue 2 years ago • 4 comments

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.
Fund with Polar

ghost avatar Jan 07 '22 12:01 ghost

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

la4de avatar Feb 19 '22 19:02 la4de

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.

ghost avatar Feb 20 '22 00:02 ghost

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)

la4de avatar Feb 23 '22 19:02 la4de

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!

sjdemartini avatar Nov 03 '23 16:11 sjdemartini