diff --git a/Makefile b/Makefile index 29c412b..ba00562 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ dev-setup: .PHONY: tests ## Run unit tests tests: - py.test graphene_django --cov=graphene_django -vv + PYTHONPATH=. py.test graphene_django --cov=graphene_django -vv .PHONY: format ## Format code format: diff --git a/graphene_django/converter.py b/graphene_django/converter.py index 2a46dff..f4775e8 100644 --- a/graphene_django/converter.py +++ b/graphene_django/converter.py @@ -302,12 +302,15 @@ def convert_onetoone_field_to_djangomodel(field, registry=None): reversed_field_name = root.__class__._meta.get_field( field_name ).remote_field.name - return _type.get_queryset( - _type._meta.model.objects.filter( - **{reversed_field_name: root.pk} - ), - info, - ).get() + try: + return _type.get_queryset( + _type._meta.model.objects.filter( + **{reversed_field_name: root.pk} + ), + info, + ).get() + except _type._meta.model.DoesNotExist: + return None return custom_resolver diff --git a/graphene_django/tests/models.py b/graphene_django/tests/models.py index e729838..4afbbbc 100644 --- a/graphene_django/tests/models.py +++ b/graphene_django/tests/models.py @@ -19,7 +19,11 @@ class Pet(models.Model): class FilmDetails(models.Model): location = models.CharField(max_length=30) film = models.OneToOneField( - "Film", on_delete=models.CASCADE, related_name="details" + "Film", + on_delete=models.CASCADE, + related_name="details", + null=True, + blank=True, ) diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index cdfbc69..42394c2 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -2062,3 +2062,74 @@ def test_should_query_nullable_foreign_key(): assert result.data["person"] == { "pets": [{"name": "Jane's dog"}], } + + +def test_should_query_nullable_one_to_one_relation_with_custom_resolver(): + class FilmType(DjangoObjectType): + class Meta: + model = Film + + @classmethod + def get_queryset(cls, queryset, info): + return queryset + + class FilmDetailsType(DjangoObjectType): + class Meta: + model = FilmDetails + + @classmethod + def get_queryset(cls, queryset, info): + return queryset + + class Query(graphene.ObjectType): + film = graphene.Field(FilmType, genre=graphene.String(required=True)) + film_details = graphene.Field( + FilmDetailsType, location=graphene.String(required=True) + ) + + def resolve_film(self, info, genre): + return Film.objects.filter(genre=genre).first() + + def resolve_film_details(self, info, location): + return FilmDetails.objects.filter(location=location).first() + + schema = graphene.Schema(query=Query) + + Film.objects.create(genre="do") + FilmDetails.objects.create(location="London") + + query_film = """ + query getFilm($genre: String!) { + film(genre: $genre) { + genre + details { + location + } + } + } + """ + + query_film_details = """ + query getFilmDetails($location: String!) { + filmDetails(location: $location) { + location + film { + genre + } + } + } + """ + + result = schema.execute(query_film, variables={"genre": "do"}) + assert not result.errors + assert result.data["film"] == { + "genre": "DO", + "details": None, + } + + result = schema.execute(query_film_details, variables={"location": "London"}) + assert not result.errors + assert result.data["filmDetails"] == { + "location": "London", + "film": None, + }