blb-ventures / strawberry-django-plus

Enhanced Strawberry GraphQL integration with Django
MIT License
178 stars 47 forks source link

Extending build in create mutations when only null=True #195

Open nour-bouzid opened 1 year ago

nour-bouzid commented 1 year ago

I have the following a model with the following field:

class Invitation(models.Model):
  ...
  department = models.ForeignKey(
          Department,
          null=True,
          on_delete=models.CASCADE,
          related_name="invitee_department",
      )

And I am trying to write a mutation for creating invitations:

@gql.django.mutation
    def create_invitation(
        self, info, input: InvitationInput
    ) -> InvitationType:
           data = vars(input)
           ...
           # for simplicity
           department = None
           data.update(
                {
                    "department": department,
                    ....
                },
            )

            invite = resolvers.create(
                info, Invitation, resolvers.parse_input(info, data)
            )
            ...
            return (cast(CustomInvitationType, invite),)

the create resolver gives the following message:

{
          "field": "department",
          "kind": "VALIDATION",
          "message": "This field cannot be blank."
        }

I was only able to solve this by adding blank=True in the field but I don't want to do this. Is there any way around this?

bellini666 commented 1 year ago

Hey @nour-bouzid ,

That's basically how django works. In your field you specified that it can be null, but the way it is now it has to receive a value for it (which can be null)

The correct way of solving this would be to add a default=None there. The blank=True is an extra keyword used by some integrations (mostly by django admin) to tell that you are not required to pass that value, and when you don't pass it the default should be used.

The introspection in this lib will make the field mandatory or not depending on one of those keywords being set: https://github.com/blb-ventures/strawberry-django-plus/blob/main/strawberry_django_plus/types.py#L328

nour-bouzid commented 1 year ago

Hey @bellini666 Thanks for the quick response! With Django's ORM it is possible to create the Invitation while passing None as a value to department e.g

Invitation.objects.create(
            department=None, ...)

in my data I am also passing department as None. What do you think?

resolvers.create(
                info, Invitation, resolvers.parse_input(info, data)
            )
bellini666 commented 1 year ago

@nour-bouzid depends on wether you want it to be optional or not

null is a special case on graphql in which, for example, String! means that you are required to pass a string, but String means you can either pass None or not pass it at all.

So if null is an option, you can't make it mandatory. If you are using strawberry.auto you need to set your model to default=None, blank=True and not passing it, or passing null to it will do the right thing.

If you don't want to mess with your model, yuu probably need to type it directly (e.g. with gql.GlobalID | None = None instead

nour-bouzid commented 1 year ago

Awesome thank you 🙂

bellini666 commented 1 year ago

@nour-bouzid can I close this issue?