strawberry-graphql / strawberry-django

Strawberry GraphQL Django extension
MIT License
394 stars 115 forks source link

Partial Update - ID not required #346

Open vocabulista opened 11 months ago

vocabulista commented 11 months ago

Describe the Bug

In gql partial updates required an ID. This is not the case in strawberry_django. Let's consider these two types:


@gql.django.partial(models.Lemma)
class LemmaPartial:
    id: relay.GlobalID
    word: gql.auto
    language: Language
    stage : Optional[DictStage]
    multiword_expression: gql.auto
    categories: Optional[List[Optional[str]]]
    related_lemmas: gql.auto
    related_meanings: gql.auto

@strawberry_django.partial(models.Lemma)
class LemmaPartial(strawberry_django.NodeInputPartial):
    id: relay.NodeID[str]
    word: strawberry.auto
    language: Language
    multiword_expression: strawberry.auto
    categories: Optional[List[Optional[str]]]
    related_lemmas: strawberry.auto
    related_meanings: strawberry.auto

How can I make mandatory the ID in strawberry_django.partial?

Upvote & Fund

Fund with Polar

bellini666 commented 11 months ago

Hi @vocabulista ,

If I understand your issue correctly, you are saying that the id is being forced to non mandatory in those inputs, correct?

I tested it here with this and it worked fine:

@strawberry_django.partial(Fruit)
class FruitPartial:
    id: strawberry.relay.GlobalID
    name: strawberry.auto

@strawberry_django.partial(Fruit)
class FruitPartial2(strawberry_django.NodeInput):
    name: strawberry.auto

@strawberry.type
class Query:
    @strawberry_django.field
    def update_fruit(self, input: FruitPartial) -> bool:  # noqa: A002
        ...

    @strawberry_django.field
    def update_fruit2(self, input: FruitPartial2) -> bool:  # noqa: A002
        ...

schema = strawberry.Schema(query=Query)

The types produced in the schema were:

input FruitPartial {
  id: GlobalID!
  name: String
}

input FruitPartial2 {
  id: GlobalID!
  name: String
}

Note that NodeInput is the type that has a mandatory GlobalID. NodeInputPartial has it as optional. This means that both FruitPartial and FruitPartial2 are basically the same

Also, in the second example you have id: relay.NodeID[str]. That won't do anything for inputs, it is mostly for types.

vocabulista commented 10 months ago

Hi @bellini666 what I needed was. It was not clear to me the relationship between NodeID in the types and GlobalID here.

@strawberry_django.partial(Fruit)
class FruitPartial:
    id: strawberry.relay.GlobalID
    name: strawberry.auto

Thanks!

bellini666 commented 10 months ago

@vocabulista NodeID will have no effects on inputs/partials. It is mostly used as a way to identify the id of the type when exposing it, so that you don't have to convert it to the global id format yourself. It is only used in types implementing the Node interface, and inputs/partials don't implement interfaces.

There's some explanation about how it works in the docs, but maybe it isn't clear about inputs.

Regarding your example, that FruitPartial would be used for an update mutation, meaning you want all of its attributes to be optional so the client can pass just what he wants to update. But the id of the fruit itself should be mandatory, because you need to identify one for it to be updated.

For this, you either use id: strawberry.relay.GlobalID or define FruitPartial inheriting from NodeInput. THe only thing that NodeInput does is define an id: strawberry.relay.GlobalID inside it, so both ways will produce the same result.

Not sure if this explanation answers your question. If not, feel free to go more deeper into the issue itself :)