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

Mutation with required foreign key GraphiQL issue

Open ccsv opened this issue 1 year ago • 7 comments

I am having issues referencing the ID for an existing foreign key while bypassing the required inputs fields for the foreign key.

Lets say I have a model

class Address(models.Model):
    name = models.CharField()

class Person(models.Model):
    name = models.CharField()
    address = models.ForeignKey('Address', on_delete=models.CASCADE, blank=False, null=False)

With the type.py file like this

@strawberry.django.type(models.Address)
class Address:
    id: auto
    name:auto

@strawberry.django.input(models.Address)
class AddressInput:
    id: auto
    name:auto

@strawberry.django.type(models.Person)
class Person:
    id: auto
    name: auto
    address:'Address'

@strawberry.django.input(models.Person)
class Person:
    id: auto
    name: auto
    address:'AddressInput'

Schema.py

@strawberry.type
class Mutation:
    createAddress: Address = mutations.create(AddressInput)
    createPerson: Person =mutations.create(PersonInput)

schema = strawberry.Schema(mutation=Mutation)

If I write a query for Person and reference an existing address in the database as an ID on the GraphiQL page like this:

mutation newPerson ($name: String!, $address:ID!){
  createPerson(data: {name: $name, address: {id: $address}}) {
    id
    name
    address {
      id
      name
    }
  }
}

It will not work because the GraphiQL requires the name field to be inputted in the foreign key.

Describe the Bug

The GraphiQL interface has requirements settings for foreign keys mutations that is not bypassed when referencing an existing id.

In the above example if I wanted to create an new Person object where I have an existing id for address it would still ask for the address name field.

System Information

  • Operating system: Windows 10
  • Strawberry version (if applicable):.117.1
  • Strawberry Django: 0.4

Additional Context

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

ccsv avatar Jul 17 '22 12:07 ccsv

Hey @ccsv ,

The issue here is that using input with auto will mark that field as mandatory. You probably need to pass partial=True to the input to make sure the name is not mandatory.

bellini666 avatar Jul 17 '22 13:07 bellini666

@bellini666 Ok it works so it is a docs issue I am going to update it. Here is the PR

ccsv avatar Jul 18 '22 07:07 ccsv

@bellini666

Ok so I changed partial=True and entered my query

mutation newPerson ($name: String!, $address:ID!){
  createPerson(data: {name: $name, address: {id: $address}}) {
    id
    name
    address {
      id
      name
    }
  }
}

Variables
{
"name": "Test",
"id": 1
}

I got back an error where the ID was being changed to a string by the GraphiQL interface. Note that I defined my id as an int without the quotes.

{
  "data": null,
  "errors": [
    {
      "message": "Field 'id' expected a number but got AddressInput(id='1', name=UNSET).",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createPerson"
      ]
    }
  ]
}

ccsv avatar Jul 18 '22 08:07 ccsv

@ccsv if I understood your code correctly, based on the way you declared the mutation at the gql mutation as address: {id: $address}}, you should be passing only the id to it. If you declared it as address: $address then you could pass the object itself containing the id.

bellini666 avatar Jul 19 '22 15:07 bellini666

@bellini666 That does not work either If I try:

mutation newPerson ($name: String!, $address:ID!){
  createPerson(data: {name: $name, address:  $address}) {
    id
    name
    address {
      id
      name
    }
  }
}

Variables
{
"name": "Test",
"id": 1
}

I get this error

{
  "data": null,
  "errors": [
    {
      "message": "Variable '$address' of type 'ID!' used in position expecting type 'AddressInput!'.",
      "locations": [
        {
          "line": 1,
          "column": 20
        },
        {
          "line": 2,
          "column": 31
        }
      ]
    }
  ]
}

You are forced to declare it as AddressInput in that case. If I try it with AddressInput!

mutation newPerson ($name: String!, $address:AddressInput!){
  createPerson(data: {name: $name, address:  $address}) {
    id
    name
    address {
      id
      name
    }
  }
}

Variables
{
"name": "Test",
"id": 1
}

I get an error because AddressInput has to be an object

{
  "data": null,
  "errors": [
    {
      "message": "Variable '$address' got invalid value 1; Expected type 'AddressInput' to be a mapping.",
      "locations": [
        {
          "line": 1,
          "column": 20
        }
      ]
    }
  ]
}

ccsv avatar Jul 20 '22 04:07 ccsv

@ccsv I may be failing to understand what you want to actually achieve here, but your errors are not bugs but actual errors because they don't match the schema you wrote for python.

If you want to pass the ID only through the variables, you should be doing this:

mutation newPerson ($name: String!, $address: ID!){
  createPerson(data: {name: $name, address: {id: $address}}) {
    id
    name
    address {
      id
      name
    }
  }
}

That because address expects an object and not an ID. Now, if you want this to work:

mutation newPerson ($name: String!, $address: AddressInput!){
  createPerson(data: {name: $name, address: $address}) {
    id
    name
    address {
      id
      name
    }
  }
}

Then you should pass {"address": {"id": 123}} to the variable (note that I'm passing an object that matches AddressInput and not only the id.

bellini666 avatar Jul 23 '22 14:07 bellini666

@bellini666 I am just doing a mutation where I set the foreign key by using an id. I did the exactly mutation query you have on your second mutation input above.

To confirm I have the foreign key I ran a query which yield this:

{
  "data": {
    "addresses": [
      {
        "id": "1",
        "name": "Test1"
      },
      {
        "id": "2",
        "name": "Test2"
      },
      {
        "id": "15",
        "name": "kkk"
      }
    ]
  }
}

So I know id =1 exist for address. Then I ran the same mutation you had above:

mutation newPerson ($name: String!, $address: AddressInput!){
  createPerson(data: {name: $name, address: $address}) {
    id
    name
    address {
      id
      name
    }
  }
}

Variables
{
  "address": {"id": 1}
}

and I got this error:

{
  "data": null,
  "errors": [
    {
      "message": "Field 'id' expected a number but got AddressInput(id='1', name=UNSET).",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createPerson"
      ]
    }
  ]
}

Note that the error has name=UNSET instead of referencing the address( "id": "1", "name": "Test1"). I don't think this is a user error. If it is my error please show me where my mistake is. I also changed the decorator for AddressInput to @strawberry.django.input(models.Address, partial=True) as recommended above.

ccsv avatar Jul 26 '22 06:07 ccsv