Skip to content

Commit

Permalink
Merge pull request #373 from jm2242/proxy-model-support
Browse files Browse the repository at this point in the history
Basic Proxy model support
  • Loading branch information
syrusakbary authored Feb 11, 2018
2 parents 41f931c + bfcfccf commit c0edb0c
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 3 deletions.
27 changes: 27 additions & 0 deletions graphene_django/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,36 @@ class Reporter(models.Model):
objects = models.Manager()
doe_objects = DoeReporterManager()

reporter_type = models.IntegerField(
'Reporter Type',
null=True,
blank=True,
choices=[(1, u'Regular'), (2, u'CNN Reporter')]
)

def __str__(self): # __unicode__ on Python 2
return "%s %s" % (self.first_name, self.last_name)

def __init__(self, *args, **kwargs):
"""
Override the init method so that during runtime, Django
can know that this object can be a CNNReporter by casting
it to the proxy model. Otherwise, as far as Django knows,
when a CNNReporter is pulled from the database, it is still
of type Reporter. This was added to test proxy model support.
"""
super(Reporter, self).__init__(*args, **kwargs)
if self.reporter_type == 2: # quick and dirty way without enums
self.__class__ = CNNReporter

class CNNReporter(Reporter):
"""
This class is a proxy model for Reporter, used for testing
proxy model support
"""
class Meta:
proxy = True


class Article(models.Model):
headline = models.CharField(max_length=100)
Expand Down
143 changes: 142 additions & 1 deletion graphene_django/tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
from ..fields import DjangoConnectionField
from ..types import DjangoObjectType
from ..settings import graphene_settings
from .models import Article, Reporter
from .models import (
Article,
CNNReporter,
Reporter,
)

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -844,6 +848,7 @@ class Query(graphene.ObjectType):
email='johndoe@example.com',
a_choice=1
)

Article.objects.create(
headline='Article Node 1',
pub_date=datetime.date.today(),
Expand Down Expand Up @@ -937,3 +942,139 @@ class Query(graphene.ObjectType):
'''
result = schema.execute(query)
assert not result.errors


def test_proxy_model_support():
"""
This test asserts that we can query for all Reporters,
even if some are of a proxy model type at runtime.
"""
class ReporterType(DjangoObjectType):

class Meta:
model = Reporter
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(ReporterType)

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 not result.errors
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
1 change: 1 addition & 0 deletions graphene_django/tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Meta:
'email',
'pets',
'a_choice',
'reporter_type'
]

assert sorted(fields[-2:]) == [
Expand Down
8 changes: 7 additions & 1 deletion graphene_django/tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_django_get_node(get):
def test_django_objecttype_map_correct_fields():
fields = Reporter._meta.fields
fields = list(fields.keys())
assert fields[:-2] == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice']
assert fields[:-2] == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice', 'reporter_type']
assert sorted(fields[-2:]) == ['articles', 'films']


Expand Down Expand Up @@ -147,6 +147,7 @@ def test_schema_representation():
email: String!
pets: [Reporter]
aChoice: ReporterAChoice!
reporterType: ReporterReporterType
articles(before: String, after: String, first: Int, last: Int): ArticleConnection
}
Expand All @@ -155,6 +156,11 @@ def test_schema_representation():
A_2
}
enum ReporterReporterType {
A_1
A_2
}
type RootQuery {
node(id: ID!): Node
}
Expand Down
3 changes: 2 additions & 1 deletion graphene_django/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ def is_type_of(cls, root, info):
raise Exception((
'Received incompatible instance "{}".'
).format(root))
model = root._meta.model

model = root._meta.model._meta.concrete_model
return model == cls._meta.model

@classmethod
Expand Down

0 comments on commit c0edb0c

Please sign in to comment.