strawberry-graphql / strawberry-django

Strawberry GraphQL Django extension
https://strawberry.rocks/docs/django
MIT License
415 stars 120 forks source link

Better typing model property #240

Open OndrejIT opened 1 year ago

OndrejIT commented 1 year ago

Hi, I have a model property in the django framework that returns a list of dict Is there any way to type it more easily than using a resolver?

Currently my code looks like this:

My django model:

class MySite:
    @property
    def player_data(self):
        data = [
            {
                "src": signed_movie_url(self.project_path, "video-1080p.mp4"),
                "size": 1080,
                "type": "video/mp4",
            },
            {
                "src": signed_movie_url(self.project_path, "video-720p.mp4"),
                "size": 720,
                "type": "video/mp4",
            },
            {
                "src": signed_movie_url(self.project_path, "video-540p.mp4"),
                "size": 540,
                "type": "video/mp4",
            },
        ]

        return data

My types:

@strawberry.type
class SiteType:
    @strawberry.type()
    class PlayerSiteDataType:
        src: str
        size: int
        type: str

    @staticmethod
    def player_data(root):
        return [SiteType.PlayerSiteDataType(**i) for i in root.player_data]

    name: str
    player_data: List[PlayerSiteDataType] = strawberry.field(resolver=player_data)

Thanks.

Upvote & Fund

Fund with Polar

OndrejIT commented 1 year ago

I'm rewriting my api in graphene_django to strawberry. Currently the code I use for graphene_django is much more readable:

class SiteType(DjangoObjectType):
    class PlayerSiteDataType(graphene.ObjectType):
        src = graphene.String()
        size = graphene.String()
        type = graphene.String()

    player_data = graphene.List(PlayerSiteDataType)

    class Meta:
        model = MySite
        fields = ("name",)
vitosamson commented 1 year ago

If it helps, I created a custom type JsonType for resolving dicts. This allows us to avoid writing custom resolvers for these sorts of objects:

def json_field():
    """
    Creates a field and resolver for fields on a json blob.
    """

    def resolver(root: dict, info: Info):
        if isinstance(info.return_type, StrawberryOptional):
            return root.get(info.python_name, None)
        return root[info.python_name]

    return strawberry.field(resolver=resolver)

@strawberry.type
class JsonType:
    """
    Models the type of a json blob, which needs specific typing for graphql.
    """

    def __init_subclass__(cls) -> None:
        for field_name in cls.__annotations__.keys():
            setattr(cls, field_name, json_field())

You would just need to have your PlayerSiteDataType subclass JsonType like so:

@strawberry.type
class SiteType:
    @strawberry.type()
    class PlayerSiteDataType(JsonType):
        src: str
        size: int
        type: str

    name: str
    player_data: List[PlayerSiteDataType]
OndrejIT commented 1 year ago

@vitosamson it looks good. Thank you!