Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge fields used in meta attribute passed on DjangoObjectType #1132

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions graphene_django/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,9 @@ class Article(models.Model):
def __str__(self): # __unicode__ on Python 2
return self.headline

@property
def headline_with_lang(self):
return "{} - {}".format(self.lang, self.headline)

class Meta:
ordering = ("headline",)
84 changes: 82 additions & 2 deletions graphene_django/tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@

import graphene
from graphene.relay import Node
from graphene.types.utils import yank_fields_from_attrs

from ..compat import IntegerRangeField, MissingType
from ..fields import DjangoConnectionField
from ..types import DjangoObjectType
from ..fields import DjangoConnectionField, DjangoListField
from ..types import DjangoObjectType, DjangoObjectTypeOptions
from ..utils import DJANGO_FILTER_INSTALLED
from .models import Article, CNNReporter, Film, FilmDetails, Reporter

Expand Down Expand Up @@ -1586,3 +1587,82 @@ class Query(graphene.ObjectType):
"allReporters": {"edges": [{"node": {"firstName": "Jane", "lastName": "Roe"}},]}
}
assert result.data == expected


def test_should_query_django_objecttype_fields_custom_meta():
class ArticleBaseType(DjangoObjectType):
class Meta:
abstract = True

@classmethod
def __init_subclass_with_meta__(cls, _meta=None, **options):
if _meta is None:
_meta = DjangoObjectTypeOptions(cls)

_meta.fields = yank_fields_from_attrs(
{"headline_with_lang": graphene.String()}, _as=graphene.Field,
)

super(ArticleBaseType, cls).__init_subclass_with_meta__(
_meta=_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
102 changes: 102 additions & 0 deletions graphene_django/tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,33 @@ class Meta:
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():
expected = dedent(
"""\
Expand Down Expand Up @@ -278,6 +305,81 @@ class Meta:
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
def test_django_objecttype_fields():
class Reporter(DjangoObjectType):
Expand Down
15 changes: 14 additions & 1 deletion graphene_django/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,26 @@ def __init_subclass_with_meta__(

if not _meta:
_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.registry = registry
_meta.filter_fields = filter_fields
_meta.filterset_class = filterset_class
_meta.fields = django_fields
_meta.connection = connection
if _meta.fields:
_meta.fields.update(django_fields)
else:
_meta.fields = django_fields

super(DjangoObjectType, cls).__init_subclass_with_meta__(
_meta=_meta, interfaces=interfaces, **options
Expand Down