Skip to content

Commit c0edb0c

Browse files
authored
Merge pull request #373 from jm2242/proxy-model-support
Basic Proxy model support
2 parents 41f931c + bfcfccf commit c0edb0c

File tree

5 files changed

+179
-3
lines changed

5 files changed

+179
-3
lines changed

graphene_django/tests/models.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,36 @@ class Reporter(models.Model):
3535
objects = models.Manager()
3636
doe_objects = DoeReporterManager()
3737

38+
reporter_type = models.IntegerField(
39+
'Reporter Type',
40+
null=True,
41+
blank=True,
42+
choices=[(1, u'Regular'), (2, u'CNN Reporter')]
43+
)
44+
3845
def __str__(self): # __unicode__ on Python 2
3946
return "%s %s" % (self.first_name, self.last_name)
4047

48+
def __init__(self, *args, **kwargs):
49+
"""
50+
Override the init method so that during runtime, Django
51+
can know that this object can be a CNNReporter by casting
52+
it to the proxy model. Otherwise, as far as Django knows,
53+
when a CNNReporter is pulled from the database, it is still
54+
of type Reporter. This was added to test proxy model support.
55+
"""
56+
super(Reporter, self).__init__(*args, **kwargs)
57+
if self.reporter_type == 2: # quick and dirty way without enums
58+
self.__class__ = CNNReporter
59+
60+
class CNNReporter(Reporter):
61+
"""
62+
This class is a proxy model for Reporter, used for testing
63+
proxy model support
64+
"""
65+
class Meta:
66+
proxy = True
67+
4168

4269
class Article(models.Model):
4370
headline = models.CharField(max_length=100)

graphene_django/tests/test_query.py

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
from ..fields import DjangoConnectionField
1414
from ..types import DjangoObjectType
1515
from ..settings import graphene_settings
16-
from .models import Article, Reporter
16+
from .models import (
17+
Article,
18+
CNNReporter,
19+
Reporter,
20+
)
1721

1822
pytestmark = pytest.mark.django_db
1923

@@ -844,6 +848,7 @@ class Query(graphene.ObjectType):
844848
email='johndoe@example.com',
845849
a_choice=1
846850
)
851+
847852
Article.objects.create(
848853
headline='Article Node 1',
849854
pub_date=datetime.date.today(),
@@ -937,3 +942,139 @@ class Query(graphene.ObjectType):
937942
'''
938943
result = schema.execute(query)
939944
assert not result.errors
945+
946+
947+
def test_proxy_model_support():
948+
"""
949+
This test asserts that we can query for all Reporters,
950+
even if some are of a proxy model type at runtime.
951+
"""
952+
class ReporterType(DjangoObjectType):
953+
954+
class Meta:
955+
model = Reporter
956+
interfaces = (Node, )
957+
use_connection = True
958+
959+
reporter_1 = Reporter.objects.create(
960+
first_name='John',
961+
last_name='Doe',
962+
email='johndoe@example.com',
963+
a_choice=1
964+
)
965+
966+
reporter_2 = CNNReporter.objects.create(
967+
first_name='Some',
968+
last_name='Guy',
969+
email='someguy@cnn.com',
970+
a_choice=1,
971+
reporter_type=2, # set this guy to be CNN
972+
)
973+
974+
class Query(graphene.ObjectType):
975+
all_reporters = DjangoConnectionField(ReporterType)
976+
977+
schema = graphene.Schema(query=Query)
978+
query = '''
979+
query ProxyModelQuery {
980+
allReporters {
981+
edges {
982+
node {
983+
id
984+
}
985+
}
986+
}
987+
}
988+
'''
989+
990+
expected = {
991+
'allReporters': {
992+
'edges': [{
993+
'node': {
994+
'id': 'UmVwb3J0ZXJUeXBlOjE=',
995+
},
996+
},
997+
{
998+
'node': {
999+
'id': 'UmVwb3J0ZXJUeXBlOjI=',
1000+
},
1001+
}
1002+
]
1003+
}
1004+
}
1005+
1006+
result = schema.execute(query)
1007+
assert not result.errors
1008+
assert result.data == expected
1009+
1010+
1011+
def test_proxy_model_fails():
1012+
"""
1013+
This test asserts that if you try to query for a proxy model,
1014+
that query will fail with:
1015+
GraphQLError('Expected value of type "CNNReporterType" but got:
1016+
CNNReporter.',)
1017+
1018+
This is because a proxy model has the identical model definition
1019+
to its superclass, and defines its behavior at runtime, rather than
1020+
at the database level. Currently, filtering objects of the proxy models'
1021+
type isn't supported. It would require a field on the model that would
1022+
represent the type, and it doesn't seem like there is a clear way to
1023+
enforce this pattern across all projects
1024+
"""
1025+
class CNNReporterType(DjangoObjectType):
1026+
1027+
class Meta:
1028+
model = CNNReporter
1029+
interfaces = (Node, )
1030+
use_connection = True
1031+
1032+
reporter_1 = Reporter.objects.create(
1033+
first_name='John',
1034+
last_name='Doe',
1035+
email='johndoe@example.com',
1036+
a_choice=1
1037+
)
1038+
1039+
reporter_2 = CNNReporter.objects.create(
1040+
first_name='Some',
1041+
last_name='Guy',
1042+
email='someguy@cnn.com',
1043+
a_choice=1,
1044+
reporter_type=2, # set this guy to be CNN
1045+
)
1046+
1047+
class Query(graphene.ObjectType):
1048+
all_reporters = DjangoConnectionField(CNNReporterType)
1049+
1050+
schema = graphene.Schema(query=Query)
1051+
query = '''
1052+
query ProxyModelQuery {
1053+
allReporters {
1054+
edges {
1055+
node {
1056+
id
1057+
}
1058+
}
1059+
}
1060+
}
1061+
'''
1062+
1063+
expected = {
1064+
'allReporters': {
1065+
'edges': [{
1066+
'node': {
1067+
'id': 'UmVwb3J0ZXJUeXBlOjE=',
1068+
},
1069+
},
1070+
{
1071+
'node': {
1072+
'id': 'UmVwb3J0ZXJUeXBlOjI=',
1073+
},
1074+
}
1075+
]
1076+
}
1077+
}
1078+
1079+
result = schema.execute(query)
1080+
assert result.errors

graphene_django/tests/test_schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Meta:
3535
'email',
3636
'pets',
3737
'a_choice',
38+
'reporter_type'
3839
]
3940

4041
assert sorted(fields[-2:]) == [

graphene_django/tests/test_types.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_django_get_node(get):
5858
def test_django_objecttype_map_correct_fields():
5959
fields = Reporter._meta.fields
6060
fields = list(fields.keys())
61-
assert fields[:-2] == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice']
61+
assert fields[:-2] == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice', 'reporter_type']
6262
assert sorted(fields[-2:]) == ['articles', 'films']
6363

6464

@@ -147,6 +147,7 @@ def test_schema_representation():
147147
email: String!
148148
pets: [Reporter]
149149
aChoice: ReporterAChoice!
150+
reporterType: ReporterReporterType
150151
articles(before: String, after: String, first: Int, last: Int): ArticleConnection
151152
}
152153
@@ -155,6 +156,11 @@ def test_schema_representation():
155156
A_2
156157
}
157158
159+
enum ReporterReporterType {
160+
A_1
161+
A_2
162+
}
163+
158164
type RootQuery {
159165
node(id: ID!): Node
160166
}

graphene_django/types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ def is_type_of(cls, root, info):
110110
raise Exception((
111111
'Received incompatible instance "{}".'
112112
).format(root))
113-
model = root._meta.model
113+
114+
model = root._meta.model._meta.concrete_model
114115
return model == cls._meta.model
115116

116117
@classmethod

0 commit comments

Comments
 (0)