Adds enhanced support for proxy models.

This commit is contained in:
Andrew Bettke 2019-03-27 17:09:25 +13:00
parent ea2cd9894f
commit 36ac5626e9
3 changed files with 37 additions and 69 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
@ -895,8 +896,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):
@ -905,11 +905,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",
@ -919,6 +925,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 = """
@ -930,14 +937,26 @@ def test_proxy_model_support():
} }
} }
} }
cnnReporters {
edges {
node {
id
}
}
}
} }
""" """
expected = { expected = {
"allReporters": { "allReporters": {
"edges": [ "edges": [
{"node": {"id": "UmVwb3J0ZXJUeXBlOjE="}}, {"node": {"id": base64.b64encode("ReporterType:{}".format(reporter.id))}},
{"node": {"id": "UmVwb3J0ZXJUeXBlOjI="}}, {"node": {"id": base64.b64encode("ReporterType:{}".format(cnn_reporter.id))}},
]
},
"cnnReporters": {
"edges": [
{"node": {"id": base64.b64encode("CNNReporterType:{}".format(cnn_reporter.id))}}
] ]
} }
} }
@ -945,65 +964,3 @@ 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

View File

@ -130,7 +130,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