Merge fields used in meta attribute passed on DjangoObjectType

This commit is contained in:
Sebastian Hernandez 2021-02-22 19:42:25 +01:00
parent 4573d3db53
commit aed2f78bdb
4 changed files with 201 additions and 3 deletions

View File

@ -115,5 +115,9 @@ class Article(models.Model):
def __str__(self): # __unicode__ on Python 2 def __str__(self): # __unicode__ on Python 2
return self.headline return self.headline
@property
def headline_with_lang(self):
return "{} - {}".format(self.lang, self.headline)
class Meta: class Meta:
ordering = ("headline",) ordering = ("headline",)

View File

@ -10,10 +10,11 @@ from py.test import raises
import graphene import graphene
from graphene.relay import Node from graphene.relay import Node
from graphene.types.utils import yank_fields_from_attrs
from ..compat import IntegerRangeField, MissingType from ..compat import IntegerRangeField, MissingType
from ..fields import DjangoConnectionField from ..fields import DjangoConnectionField, DjangoListField
from ..types import DjangoObjectType from ..types import DjangoObjectType, DjangoObjectTypeOptions
from ..utils import DJANGO_FILTER_INSTALLED from ..utils import DJANGO_FILTER_INSTALLED
from .models import Article, CNNReporter, Film, FilmDetails, Reporter from .models import Article, CNNReporter, Film, FilmDetails, Reporter
@ -1586,3 +1587,81 @@ def test_connection_should_allow_offset_filtering_with_after():
"allReporters": {"edges": [{"node": {"firstName": "Jane", "lastName": "Roe"}},]} "allReporters": {"edges": [{"node": {"firstName": "Jane", "lastName": "Roe"}},]}
} }
assert result.data == expected assert result.data == expected
def test_should_query_django_objecttype_fields_custom_meta():
class ArticleTypeOptions(DjangoObjectTypeOptions):
"""Article Type Options with extra fields"""
fields = yank_fields_from_attrs(
{"headline_with_lang": graphene.String()}, _as=graphene.Field,
)
class ArticleBaseType(DjangoObjectType):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, **options):
options.setdefault("_meta", ArticleTypeOptions(cls))
super(ArticleBaseType, cls).__init_subclass_with_meta__(**options)
class ArticleCustomType(ArticleBaseType):
class Meta:
model = Article
fields = (
"headline",
"lang",
"headline_with_lang",
)
class Query(graphene.ObjectType):
all_articles = DjangoListField(ArticleCustomType)
r = Reporter.objects.create(
first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
)
Article.objects.create(
headline="Article Node 1",
pub_date=datetime.date.today(),
pub_date_time=datetime.datetime.now(),
reporter=r,
editor=r,
lang="es",
)
Article.objects.create(
headline="Article Node 2",
pub_date=datetime.date.today(),
pub_date_time=datetime.datetime.now(),
reporter=r,
editor=r,
lang="en",
)
schema = graphene.Schema(query=Query)
query = """
query GetAllArticles {
allArticles {
headline
lang
headlineWithLang
}
}
"""
result = schema.execute(query)
assert not result.errors
expected = {
"allArticles": [
{
"headline": "Article Node 1",
"lang": "ES",
"headlineWithLang": "es - Article Node 1",
},
{
"headline": "Article Node 2",
"lang": "EN",
"headlineWithLang": "en - Article Node 2",
},
]
}
assert result.data == expected

View File

@ -114,6 +114,33 @@ def test_django_objecttype_with_custom_meta():
assert isinstance(Article._meta, ArticleTypeOptions) assert isinstance(Article._meta, ArticleTypeOptions)
def test_django_objecttype_with_custom_meta_fields():
class ArticleTypeOptions(DjangoObjectTypeOptions):
"""Article Type Options with extra fields"""
fields = {"headline_with_lang": String()}
class ArticleType(DjangoObjectType):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, **options):
options.setdefault("_meta", ArticleTypeOptions(cls))
super(ArticleType, cls).__init_subclass_with_meta__(**options)
class Article(ArticleType):
class Meta:
model = ArticleModel
fields = "__all__"
headline_with_lang_field = Article._meta.fields.get("headline_with_lang")
assert isinstance(Article._meta, ArticleTypeOptions)
assert headline_with_lang_field is not None
assert isinstance(headline_with_lang_field, String)
def test_schema_representation(): def test_schema_representation():
expected = dedent( expected = dedent(
"""\ """\
@ -278,6 +305,81 @@ def test_django_objecttype_only_fields():
assert fields == ["id", "email", "films"] assert fields == ["id", "email", "films"]
@with_local_registry
def test_django_objecttype_fields_custom_meta_fields():
class ArticleTypeOptions(DjangoObjectTypeOptions):
"""Article Type Options with extra fields"""
fields = {"headline_with_lang": String()}
class ArticleType(DjangoObjectType):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, **options):
options.setdefault("_meta", ArticleTypeOptions(cls))
super(ArticleType, cls).__init_subclass_with_meta__(**options)
class Article(ArticleType):
class Meta:
model = ArticleModel
fields = ("editor", "lang", "importance")
fields = list(Article._meta.fields.keys())
assert fields == ["editor", "lang", "importance"]
@with_local_registry
def test_django_objecttype_fields_custom_meta_fields_include():
class ArticleTypeOptions(DjangoObjectTypeOptions):
"""Article Type Options with extra fields"""
fields = {"headline_with_lang": String()}
class ArticleType(DjangoObjectType):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, **options):
options.setdefault("_meta", ArticleTypeOptions(cls))
super(ArticleType, cls).__init_subclass_with_meta__(**options)
class Article(ArticleType):
class Meta:
model = ArticleModel
fields = ("headline_with_lang", "editor", "lang", "importance")
fields = list(Article._meta.fields.keys())
assert fields == ["headline_with_lang", "editor", "lang", "importance"]
@with_local_registry
def test_django_objecttype_fields_custom_meta_fields_all():
class ArticleTypeOptions(DjangoObjectTypeOptions):
"""Article Type Options with extra fields"""
fields = {"headline_with_lang": String()}
class ArticleType(DjangoObjectType):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, **options):
options.setdefault("_meta", ArticleTypeOptions(cls))
super(ArticleType, cls).__init_subclass_with_meta__(**options)
class Article(ArticleType):
class Meta:
model = ArticleModel
fields = "__all__"
fields = list(Article._meta.fields.keys())
assert len(fields) == len(ArticleModel._meta.get_fields()) + 1
@with_local_registry @with_local_registry
def test_django_objecttype_fields(): def test_django_objecttype_fields():
class Reporter(DjangoObjectType): class Reporter(DjangoObjectType):

View File

@ -256,13 +256,26 @@ class DjangoObjectType(ObjectType):
if not _meta: if not _meta:
_meta = DjangoObjectTypeOptions(cls) _meta = DjangoObjectTypeOptions(cls)
elif _meta.fields:
# Exclude previous meta fields that are not in fields or are in exclude
only_fields = fields is not None and fields != ALL_FIELDS
exclude_fields = exclude is not None
if only_fields or exclude_fields:
for name in list(_meta.fields.keys()):
if (only_fields and name not in fields) or (
exclude_fields and name in exclude
):
_meta.fields.pop(name)
_meta.model = model _meta.model = model
_meta.registry = registry _meta.registry = registry
_meta.filter_fields = filter_fields _meta.filter_fields = filter_fields
_meta.filterset_class = filterset_class _meta.filterset_class = filterset_class
_meta.fields = django_fields
_meta.connection = connection _meta.connection = connection
if _meta.fields:
_meta.fields.update(django_fields)
else:
_meta.fields = django_fields
super(DjangoObjectType, cls).__init_subclass_with_meta__( super(DjangoObjectType, cls).__init_subclass_with_meta__(
_meta=_meta, interfaces=interfaces, **options _meta=_meta, interfaces=interfaces, **options