Merge pull request #603 from abettke/fix/enhanced-proxy-model-support

Adds enhanced support for proxy models.
This commit is contained in:
Mel van Londen 2019-06-09 16:47:10 -07:00 committed by GitHub
commit b271b259bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 70 deletions

View File

@ -65,6 +65,11 @@ class Reporter(models.Model):
self.__class__ = CNNReporter self.__class__ = CNNReporter
class CNNReporterManager(models.Manager):
def get_queryset(self):
return super(CNNReporterManager, self).get_queryset().filter(reporter_type=2)
class CNNReporter(Reporter): class CNNReporter(Reporter):
""" """
This class is a proxy model for Reporter, used for testing This class is a proxy model for Reporter, used for testing
@ -74,6 +79,8 @@ class CNNReporter(Reporter):
class Meta: class Meta:
proxy = True proxy = True
objects = CNNReporterManager()
class Article(models.Model): class Article(models.Model):
headline = models.CharField(max_length=100) headline = models.CharField(max_length=100)

View File

@ -1,3 +1,4 @@
import base64
import datetime import datetime
import pytest import pytest
@ -7,6 +8,7 @@ from py.test import raises
from django.db.models import Q from django.db.models import Q
from graphql_relay import to_global_id
import graphene import graphene
from graphene.relay import Node from graphene.relay import Node
@ -951,8 +953,7 @@ def test_should_handle_inherited_choices():
def test_proxy_model_support(): def test_proxy_model_support():
""" """
This test asserts that we can query for all Reporters, This test asserts that we can query for all Reporters and proxied Reporters.
even if some are of a proxy model type at runtime.
""" """
class ReporterType(DjangoObjectType): class ReporterType(DjangoObjectType):
@ -961,11 +962,17 @@ def test_proxy_model_support():
interfaces = (Node,) interfaces = (Node,)
use_connection = True use_connection = True
reporter_1 = Reporter.objects.create( class CNNReporterType(DjangoObjectType):
class Meta:
model = CNNReporter
interfaces = (Node,)
use_connection = True
reporter = Reporter.objects.create(
first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1 first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
) )
reporter_2 = CNNReporter.objects.create( cnn_reporter = CNNReporter.objects.create(
first_name="Some", first_name="Some",
last_name="Guy", last_name="Guy",
email="someguy@cnn.com", email="someguy@cnn.com",
@ -975,6 +982,7 @@ def test_proxy_model_support():
class Query(graphene.ObjectType): class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType) all_reporters = DjangoConnectionField(ReporterType)
cnn_reporters = DjangoConnectionField(CNNReporterType)
schema = graphene.Schema(query=Query) schema = graphene.Schema(query=Query)
query = """ query = """
@ -986,14 +994,26 @@ def test_proxy_model_support():
} }
} }
} }
cnnReporters {
edges {
node {
id
}
}
}
} }
""" """
expected = { expected = {
"allReporters": { "allReporters": {
"edges": [ "edges": [
{"node": {"id": "UmVwb3J0ZXJUeXBlOjE="}}, {"node": {"id": to_global_id("ReporterType", reporter.id)}},
{"node": {"id": "UmVwb3J0ZXJUeXBlOjI="}}, {"node": {"id": to_global_id("ReporterType", cnn_reporter.id)}},
]
},
"cnnReporters": {
"edges": [
{"node": {"id": to_global_id("CNNReporterType", cnn_reporter.id)}}
] ]
} }
} }
@ -1001,69 +1021,7 @@ def test_proxy_model_support():
result = schema.execute(query) result = schema.execute(query)
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected
def test_proxy_model_fails():
"""
This test asserts that if you try to query for a proxy model,
that query will fail with:
GraphQLError('Expected value of type "CNNReporterType" but got:
CNNReporter.',)
This is because a proxy model has the identical model definition
to its superclass, and defines its behavior at runtime, rather than
at the database level. Currently, filtering objects of the proxy models'
type isn't supported. It would require a field on the model that would
represent the type, and it doesn't seem like there is a clear way to
enforce this pattern across all projects
"""
class CNNReporterType(DjangoObjectType):
class Meta:
model = CNNReporter
interfaces = (Node,)
use_connection = True
reporter_1 = Reporter.objects.create(
first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
)
reporter_2 = CNNReporter.objects.create(
first_name="Some",
last_name="Guy",
email="someguy@cnn.com",
a_choice=1,
reporter_type=2, # set this guy to be CNN
)
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(CNNReporterType)
schema = graphene.Schema(query=Query)
query = """
query ProxyModelQuery {
allReporters {
edges {
node {
id
}
}
}
}
"""
expected = {
"allReporters": {
"edges": [
{"node": {"id": "UmVwb3J0ZXJUeXBlOjE="}},
{"node": {"id": "UmVwb3J0ZXJUeXBlOjI="}},
]
}
}
result = schema.execute(query)
assert result.errors
def test_should_resolve_get_queryset_connectionfields(): def test_should_resolve_get_queryset_connectionfields():
reporter_1 = Reporter.objects.create( reporter_1 = Reporter.objects.create(

View File

@ -131,7 +131,11 @@ class DjangoObjectType(ObjectType):
if not is_valid_django_model(type(root)): if not is_valid_django_model(type(root)):
raise Exception(('Received incompatible instance "{}".').format(root)) raise Exception(('Received incompatible instance "{}".').format(root))
model = root._meta.model._meta.concrete_model if cls._meta.model._meta.proxy:
model = root._meta.model
else:
model = root._meta.model._meta.concrete_model
return model == cls._meta.model return model == cls._meta.model
@classmethod @classmethod