mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-01-31 03:34:13 +03:00
on_delete param where needed due to django 2 requirements
added support for querying a model with objects that may be a proxy model, fixed and added tests a few style changes
This commit is contained in:
parent
b54e02c9ba
commit
0b103417f9
|
@ -5,7 +5,13 @@ from django.db import models
|
|||
|
||||
class Character(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
ship = models.ForeignKey('Ship', blank=True, null=True, related_name='characters')
|
||||
ship = models.ForeignKey(
|
||||
'Ship',
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='characters'
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -13,7 +19,10 @@ class Character(models.Model):
|
|||
|
||||
class Faction(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
hero = models.ForeignKey(Character)
|
||||
hero = models.ForeignKey(
|
||||
Character,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -21,7 +30,11 @@ class Faction(models.Model):
|
|||
|
||||
class Ship(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
faction = models.ForeignKey(Faction, related_name='ships')
|
||||
faction = models.ForeignKey(
|
||||
Faction,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='ships'
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -15,7 +15,11 @@ class Pet(models.Model):
|
|||
|
||||
class FilmDetails(models.Model):
|
||||
location = models.CharField(max_length=30)
|
||||
film = models.OneToOneField('Film', related_name='details')
|
||||
film = models.OneToOneField(
|
||||
'Film',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='details'
|
||||
)
|
||||
|
||||
|
||||
class Film(models.Model):
|
||||
|
@ -30,15 +34,49 @@ class Reporter(models.Model):
|
|||
pets = models.ManyToManyField('self')
|
||||
a_choice = models.CharField(max_length=30, choices=CHOICES)
|
||||
|
||||
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)
|
||||
pub_date = models.DateField()
|
||||
reporter = models.ForeignKey(Reporter, related_name='articles')
|
||||
editor = models.ForeignKey(Reporter, related_name='edited_articles_+')
|
||||
reporter = models.ForeignKey(
|
||||
Reporter,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='articles'
|
||||
)
|
||||
editor = models.ForeignKey(
|
||||
Reporter,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='edited_articles_+'
|
||||
)
|
||||
lang = models.CharField(max_length=2, help_text='Language', choices=[
|
||||
('es', 'Spanish'),
|
||||
('en', 'English')
|
||||
|
|
|
@ -13,7 +13,11 @@ from ..compat import MissingType, JSONField
|
|||
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
|
||||
|
||||
|
@ -689,6 +693,7 @@ def test_should_query_dataloader_fields():
|
|||
email='johndoe@example.com',
|
||||
a_choice=1
|
||||
)
|
||||
|
||||
Article.objects.create(
|
||||
headline='Article Node 1',
|
||||
pub_date=datetime.date.today(),
|
||||
|
@ -780,3 +785,139 @@ def test_should_handle_inherited_choices():
|
|||
'''
|
||||
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
|
||||
|
|
|
@ -35,6 +35,7 @@ def test_should_map_fields_correctly():
|
|||
'email',
|
||||
'pets',
|
||||
'a_choice',
|
||||
'reporter_type'
|
||||
]
|
||||
|
||||
assert sorted(fields[-2:]) == [
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
||||
|
@ -124,6 +124,7 @@ type Reporter {
|
|||
email: String!
|
||||
pets: [Reporter]
|
||||
aChoice: ReporterAChoice!
|
||||
reporterType: ReporterReporterType
|
||||
articles(before: String, after: String, first: Int, last: Int): ArticleConnection
|
||||
}
|
||||
|
||||
|
@ -132,6 +133,11 @@ enum ReporterAChoice {
|
|||
A_2
|
||||
}
|
||||
|
||||
enum ReporterReporterType {
|
||||
A_1
|
||||
A_2
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
node(id: ID!): Node
|
||||
}
|
||||
|
|
|
@ -108,7 +108,8 @@ class DjangoObjectType(ObjectType):
|
|||
raise Exception((
|
||||
'Received incompatible instance "{}".'
|
||||
).format(root))
|
||||
model = root._meta.model
|
||||
|
||||
model = root._meta.model._meta.concrete_model
|
||||
return model == cls._meta.model
|
||||
|
||||
@classmethod
|
||||
|
|
Loading…
Reference in New Issue
Block a user