Warn if fields or exclude are not defined on DjangoObjectType (#981)

This commit is contained in:
Radosław Kowalski 2020-06-11 12:09:52 +02:00 committed by GitHub
parent 17146f9b01
commit 1f752b6cad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 150 additions and 1 deletions

View File

@ -111,6 +111,7 @@ If you are using ``DjangoObjectType`` you can define a custom `get_queryset`.
class PostNode(DjangoObjectType):
class Meta:
model = Post
fields = '__all__'
@classmethod
def get_queryset(cls, queryset, info):

View File

@ -45,6 +45,7 @@ For example:
class Meta:
# Assume you have an Animal model defined with the following fields
model = Animal
fields = '__all__'
filter_fields = ['name', 'genus', 'is_domesticated']
interfaces = (relay.Node, )
@ -75,6 +76,7 @@ You can also make more complex lookup types available:
class AnimalNode(DjangoObjectType):
class Meta:
model = Animal
fields = '__all__'
# Provide more complex lookup types
filter_fields = {
'name': ['exact', 'icontains', 'istartswith'],
@ -116,6 +118,7 @@ create your own ``FilterSet``. You can pass it directly as follows:
class Meta:
# Assume you have an Animal model defined with the following fields
model = Animal
fields = '__all__'
filter_fields = ['name', 'genus', 'is_domesticated']
interfaces = (relay.Node, )
@ -179,6 +182,7 @@ in unison with the ``filter_fields`` parameter:
class AnimalNode(DjangoObjectType):
class Meta:
model = Animal
fields = '__all__'
filterset_class = AnimalFilter
interfaces = (relay.Node, )
@ -236,6 +240,7 @@ Extend the tuple of fields if you want to order by more than one field.
class Meta:
name = 'Group'
model = GroupModel
fields = '__all__'
interfaces = (relay.Node,)
def resolve_users(self, info, **kwargs):

View File

@ -25,6 +25,7 @@ Simple example
class QuestionType(DjangoObjectType):
class Meta:
model = Question
fields = '__all__'
class QuestionMutation(graphene.Mutation):
@ -90,6 +91,7 @@ DjangoModelFormMutation
class PetType(DjangoObjectType):
class Meta:
model = Pet
fields = '__all__'
class PetMutation(DjangoModelFormMutation):
pet = Field(PetType)

View File

@ -28,6 +28,7 @@ Full example
class QuestionType(DjangoObjectType):
class Meta:
model = Question
fields = '__all__'
class Query:
@ -53,6 +54,9 @@ all fields that should be exposed using the fields attribute.
This will make it less likely to result in unintentionally exposing data when
your models change.
Setting neither ``fields`` nor ``exclude`` is deprecated and will raise a warning, you should at least explicitly make
``DjangoObjectType`` include all fields in the model as described below.
``fields``
~~~~~~~~~~
@ -127,6 +131,7 @@ For example the following ``Model`` and ``DjangoObjectType``:
class Pet(DjangoObjectType):
class Meta:
model = PetModel
fields = '__all__'
Results in the following GraphQL schema definition:
@ -151,6 +156,7 @@ You can disable this automatic conversion by setting
class Pet(DjangoObjectType):
class Meta:
model = PetModel
fields = '__all__'
convert_choices_to_enum = False
.. code::
@ -168,6 +174,7 @@ automatically converted into enums:
class Pet(DjangoObjectType):
class Meta:
model = PetModel
fields = '__all__'
convert_choices_to_enum = ['kind']
**Note:** Setting ``convert_choices_to_enum = []`` is the same as setting it to
@ -206,6 +213,7 @@ need to create the most basic class for this to work:
class CategoryType(DjangoObjectType):
class Meta:
model = Category
fields = '__all__'
.. _django-objecttype-get-queryset:
@ -224,6 +232,7 @@ Use this to control filtering on the ObjectType level instead of the Query objec
class QuestionType(DjangoObjectType):
class Meta:
model = Question
fields = '__all__'
@classmethod
def get_queryset(cls, queryset, info):
@ -347,6 +356,7 @@ the core graphene pages for more information on customizing the Relay experience
class QuestionType(DjangoObjectType):
class Meta:
model = Question
fields = '__all__'
interfaces = (relay.Node,)

View File

@ -8,11 +8,13 @@
class CategoryType(DjangoObjectType):
class Meta:
model = Category
fields = '__all__'
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
fields = '__all__'
class Query(object):

View File

@ -157,11 +157,13 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
class CategoryType(DjangoObjectType):
class Meta:
model = Category
fields = '__all__'
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
fields = '__all__'
class Query(object):

View File

@ -130,6 +130,7 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
class CategoryNode(DjangoObjectType):
class Meta:
model = Category
fields = '__all__'
filter_fields = ['name', 'ingredients']
interfaces = (relay.Node, )
@ -137,6 +138,7 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
class IngredientNode(DjangoObjectType):
class Meta:
model = Ingredient
fields = '__all__'
# Allow for some more advanced filtering here
filter_fields = {
'name': ['exact', 'icontains', 'istartswith'],

View File

@ -7,11 +7,13 @@ from .models import Category, Ingredient
class CategoryType(DjangoObjectType):
class Meta:
model = Category
fields = "__all__"
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
fields = "__all__"
class Query(object):

View File

@ -7,11 +7,13 @@ from .models import Recipe, RecipeIngredient
class RecipeType(DjangoObjectType):
class Meta:
model = Recipe
fields = "__all__"
class RecipeIngredientType(DjangoObjectType):
class Meta:
model = RecipeIngredient
fields = "__all__"
class Query(object):

View File

@ -10,6 +10,7 @@ class CategoryNode(DjangoObjectType):
class Meta:
model = Category
interfaces = (Node,)
fields = "__all__"
filter_fields = ["name", "ingredients"]
@ -18,6 +19,7 @@ class IngredientNode(DjangoObjectType):
model = Ingredient
# Allow for some more advanced filtering here
interfaces = (Node,)
fields = "__all__"
filter_fields = {
"name": ["exact", "icontains", "istartswith"],
"notes": ["exact", "icontains"],

View File

@ -8,6 +8,7 @@ class RecipeNode(DjangoObjectType):
class Meta:
model = Recipe
interfaces = (Node,)
fields = "__all__"
filter_fields = ["title", "amounts"]
@ -16,6 +17,7 @@ class RecipeIngredientNode(DjangoObjectType):
model = RecipeIngredient
# Allow for some more advanced filtering here
interfaces = (Node,)
fields = "__all__"
filter_fields = {
"ingredient__name": ["exact", "icontains", "istartswith"],
"recipe": ["exact"],

View File

@ -12,6 +12,7 @@ class Ship(DjangoObjectType):
class Meta:
model = ShipModel
interfaces = (relay.Node,)
fields = "__all__"
@classmethod
def get_node(cls, info, id):
@ -22,12 +23,14 @@ class Ship(DjangoObjectType):
class Character(DjangoObjectType):
class Meta:
model = CharacterModel
fields = "__all__"
class Faction(DjangoObjectType):
class Meta:
model = FactionModel
interfaces = (relay.Node,)
fields = "__all__"
@classmethod
def get_node(cls, info, id):

View File

@ -21,6 +21,7 @@ def test_should_query_field():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
@ -65,6 +66,7 @@ def test_should_query_nested_field():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
@ -130,6 +132,7 @@ def test_should_query_list():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = graphene.List(ReporterType)
@ -172,6 +175,7 @@ def test_should_query_connection():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType)
@ -220,6 +224,7 @@ def test_should_query_connectionfilter():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoFilterConnectionField(ReporterType, fields=["last_name"])

View File

@ -41,17 +41,20 @@ if DJANGO_FILTER_INSTALLED:
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filter_fields = ("headline",)
class ReporterNode(DjangoObjectType):
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class PetNode(DjangoObjectType):
class Meta:
model = Pet
interfaces = (Node,)
fields = "__all__"
def get_args(field):
@ -189,6 +192,7 @@ def test_filter_filterset_information_on_meta():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ["first_name", "articles"]
field = DjangoFilterConnectionField(ReporterFilterNode)
@ -201,12 +205,14 @@ def test_filter_filterset_information_on_meta_related():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ["first_name", "articles"]
class ArticleFilterNode(DjangoObjectType):
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filter_fields = ["headline", "reporter"]
class Query(ObjectType):
@ -233,6 +239,7 @@ def test_filter_filterset_class_filter_fields_exception():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filterset_class = ReporterFilter
filter_fields = ["first_name", "articles"]
@ -247,6 +254,7 @@ def test_filter_filterset_class_information_on_meta():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filterset_class = ReporterFilter
field = DjangoFilterConnectionField(ReporterFilterNode)
@ -269,12 +277,14 @@ def test_filter_filterset_class_information_on_meta_related():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filterset_class = ReporterFilter
class ArticleFilterNode(DjangoObjectType):
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filterset_class = ArticleFilter
class Query(ObjectType):
@ -294,12 +304,14 @@ def test_filter_filterset_related_results():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ["first_name", "articles"]
class ArticleFilterNode(DjangoObjectType):
class Meta:
interfaces = (Node,)
model = Article
fields = "__all__"
filter_fields = ["headline", "reporter"]
class Query(ObjectType):
@ -451,6 +463,7 @@ def test_filter_filterset_related_results_with_filter():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = {"first_name": ["icontains"]}
class Query(ObjectType):
@ -496,6 +509,7 @@ def test_recursive_filter_connection():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(ObjectType):
all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
@ -521,11 +535,13 @@ def test_should_query_filter_node_limit():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class ArticleType(DjangoObjectType):
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filter_fields = ("lang",)
class Query(ObjectType):
@ -610,6 +626,7 @@ def test_order_by_is_perserved():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ()
class Query(ObjectType):
@ -676,6 +693,7 @@ def test_annotation_is_preserved():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ()
class Query(ObjectType):
@ -718,6 +736,7 @@ def test_annotation_with_only():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ()
class Query(ObjectType):
@ -758,6 +777,7 @@ def test_node_get_queryset_is_called():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
filter_fields = ()
@classmethod
@ -954,6 +974,7 @@ def test_filter_filterset_based_on_mixin():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class NewArticleFilterNode(DjangoObjectType):
viewer = Field(NewReporterNode)
@ -961,6 +982,7 @@ def test_filter_filterset_based_on_mixin():
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filterset_class = NewArticleFilter
def resolve_viewer(self, info):

View File

@ -27,12 +27,14 @@ class ParentType(DjangoObjectType):
class Meta:
model = MyFakeParentModel
interfaces = (graphene.relay.Node,)
fields = "__all__"
class ChildType(DjangoObjectType):
class Meta:
model = MyFakeChildModel
interfaces = (graphene.relay.Node,)
fields = "__all__"
class MyModelChildSerializer(serializers.ModelSerializer):

View File

@ -165,6 +165,7 @@ def test_nested_model():
class MyFakeModelGrapheneType(DjangoObjectType):
class Meta:
model = MyFakeModel
fields = "__all__"
class MyMutation(SerializerMutation):
class Meta:

View File

@ -9,6 +9,7 @@ class Character(DjangoObjectType):
class Meta:
model = Reporter
interfaces = (relay.Node,)
fields = "__all__"
def get_node(self, info, id):
pass
@ -20,6 +21,7 @@ class Human(DjangoObjectType):
class Meta:
model = Article
interfaces = (relay.Node,)
fields = "__all__"
def resolve_raises(self, info):
raise Exception("This field should raise exception")

View File

@ -245,6 +245,7 @@ def test_should_manytomany_convert_connectionorlist_list():
class A(DjangoObjectType):
class Meta:
model = Reporter
fields = "__all__"
graphene_field = convert_django_field(
Reporter._meta.local_many_to_many[0], A._meta.registry
@ -265,6 +266,7 @@ def test_should_manytomany_convert_connectionorlist_connection():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
graphene_field = convert_django_field(
Reporter._meta.local_many_to_many[0], A._meta.registry
@ -279,6 +281,7 @@ def test_should_manytoone_convert_connectionorlist():
class A(DjangoObjectType):
class Meta:
model = Article
fields = "__all__"
graphene_field = convert_django_field(Reporter.articles.rel, A._meta.registry)
assert isinstance(graphene_field, graphene.Dynamic)
@ -295,6 +298,7 @@ def test_should_onetoone_reverse_convert_model():
class A(DjangoObjectType):
class Meta:
model = FilmDetails
fields = "__all__"
graphene_field = convert_django_field(Film.details.related, A._meta.registry)
assert isinstance(graphene_field, graphene.Dynamic)

View File

@ -89,6 +89,7 @@ def test_should_query_well():
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
fields = "__all__"
class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
@ -130,6 +131,7 @@ def test_should_query_postgres_fields():
class EventType(DjangoObjectType):
class Meta:
model = Event
fields = "__all__"
class Query(graphene.ObjectType):
event = graphene.Field(EventType)
@ -171,6 +173,7 @@ def test_should_node():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
@classmethod
def get_node(cls, info, id):
@ -183,6 +186,7 @@ def test_should_node():
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
@classmethod
def get_node(cls, info, id):
@ -253,11 +257,13 @@ def test_should_query_onetoone_fields():
class Meta:
model = Film
interfaces = (Node,)
fields = "__all__"
class FilmDetailsNode(DjangoObjectType):
class Meta:
model = FilmDetails
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
film = graphene.Field(FilmNode)
@ -352,6 +358,7 @@ def test_should_keep_annotations():
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filter_fields = ("lang",)
class Query(graphene.ObjectType):
@ -405,11 +412,13 @@ def test_should_query_node_filtering():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class ArticleType(DjangoObjectType):
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filter_fields = ("lang",)
class Query(graphene.ObjectType):
@ -483,6 +492,7 @@ def test_should_query_node_filtering_with_distinct_queryset():
class Meta:
model = Film
interfaces = (Node,)
fields = "__all__"
filter_fields = ("genre",)
class Query(graphene.ObjectType):
@ -527,11 +537,13 @@ def test_should_query_node_multiple_filtering():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class ArticleType(DjangoObjectType):
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
filter_fields = ("lang", "headline")
class Query(graphene.ObjectType):
@ -612,6 +624,7 @@ def test_should_enforce_first_or_last(graphene_settings):
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType)
@ -651,6 +664,7 @@ def test_should_error_if_first_is_greater_than_max(graphene_settings):
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType)
@ -692,6 +706,7 @@ def test_should_error_if_last_is_greater_than_max(graphene_settings):
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType)
@ -733,6 +748,7 @@ def test_should_query_promise_connectionfields():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType)
@ -770,6 +786,7 @@ def test_should_query_connectionfields_with_last():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType)
@ -811,6 +828,7 @@ def test_should_query_connectionfields_with_manager():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
all_reporters = DjangoConnectionField(ReporterType, on="doe_objects")
@ -857,12 +875,14 @@ def test_should_query_dataloader_fields():
class Meta:
model = Article
interfaces = (Node,)
fields = "__all__"
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
interfaces = (Node,)
use_connection = True
fields = "__all__"
articles = DjangoConnectionField(ArticleType)
@ -947,10 +967,12 @@ def test_should_handle_inherited_choices():
class BaseType(DjangoObjectType):
class Meta:
model = BaseModel
fields = "__all__"
class ChildType(DjangoObjectType):
class Meta:
model = ChildModel
fields = "__all__"
class Query(graphene.ObjectType):
base = graphene.Field(BaseType)
@ -978,12 +1000,14 @@ def test_proxy_model_support():
model = Reporter
interfaces = (Node,)
use_connection = True
fields = "__all__"
class CNNReporterType(DjangoObjectType):
class Meta:
model = CNNReporter
interfaces = (Node,)
use_connection = True
fields = "__all__"
reporter = Reporter.objects.create(
first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
@ -1056,6 +1080,7 @@ def test_should_resolve_get_queryset_connectionfields():
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
@classmethod
def get_queryset(cls, queryset, info):
@ -1089,6 +1114,7 @@ def test_should_preserve_prefetch_related(django_assert_num_queries):
class Meta:
model = Reporter
interfaces = (graphene.relay.Node,)
fields = "__all__"
class FilmType(DjangoObjectType):
reporters = DjangoConnectionField(ReporterType)
@ -1096,6 +1122,7 @@ def test_should_preserve_prefetch_related(django_assert_num_queries):
class Meta:
model = Film
interfaces = (graphene.relay.Node,)
fields = "__all__"
class Query(graphene.ObjectType):
films = DjangoConnectionField(FilmType)
@ -1141,6 +1168,7 @@ def test_should_preserve_annotations():
class Meta:
model = Reporter
interfaces = (graphene.relay.Node,)
fields = "__all__"
class FilmType(DjangoObjectType):
reporters = DjangoConnectionField(ReporterType)
@ -1149,6 +1177,7 @@ def test_should_preserve_annotations():
class Meta:
model = Film
interfaces = (graphene.relay.Node,)
fields = "__all__"
class Query(graphene.ObjectType):
films = DjangoConnectionField(FilmType)

View File

@ -9,7 +9,7 @@ def test_should_raise_if_no_model():
with raises(Exception) as excinfo:
class Character1(DjangoObjectType):
pass
fields = "__all__"
assert "valid Django Model" in str(excinfo.value)
@ -20,6 +20,7 @@ def test_should_raise_if_model_is_invalid():
class Character2(DjangoObjectType):
class Meta:
model = 1
fields = "__all__"
assert "valid Django Model" in str(excinfo.value)
@ -29,6 +30,7 @@ def test_should_map_fields_correctly():
class Meta:
model = Reporter
registry = Registry()
fields = "__all__"
fields = list(ReporterType2._meta.fields.keys())
assert fields[:-2] == [

View File

@ -19,6 +19,7 @@ class Reporter(DjangoObjectType):
class Meta:
model = ReporterModel
fields = "__all__"
class ArticleConnection(Connection):
@ -40,6 +41,7 @@ class Article(DjangoObjectType):
model = ArticleModel
interfaces = (Node,)
connection_class = ArticleConnection
fields = "__all__"
class RootQuery(ObjectType):
@ -106,6 +108,7 @@ def test_django_objecttype_with_custom_meta():
class Article(ArticleType):
class Meta:
model = ArticleModel
fields = "__all__"
assert isinstance(Article._meta, ArticleTypeOptions)
@ -449,6 +452,37 @@ def test_django_objecttype_exclude_fields_exist_on_model():
assert len(record) == 0
@with_local_registry
def test_django_objecttype_neither_fields_nor_exclude():
with pytest.warns(
DeprecationWarning,
match=r"Creating a DjangoObjectType without either the `fields` "
"or the `exclude` option is deprecated.",
):
class Reporter(DjangoObjectType):
class Meta:
model = ReporterModel
with pytest.warns(None) as record:
class Reporter2(DjangoObjectType):
class Meta:
model = ReporterModel
fields = ["email"]
assert len(record) == 0
with pytest.warns(None) as record:
class Reporter3(DjangoObjectType):
class Meta:
model = ReporterModel
exclude = ["email"]
assert len(record) == 0
def custom_enum_name(field):
return "CustomEnum{}".format(field.name.title())
@ -473,6 +507,7 @@ class TestDjangoObjectType:
class Meta:
model = PetModel
convert_choices_to_enum = False
fields = "__all__"
class Query(ObjectType):
pet = Field(Pet)
@ -498,6 +533,7 @@ class TestDjangoObjectType:
class Meta:
model = PetModel
convert_choices_to_enum = ["kind"]
fields = "__all__"
class Query(ObjectType):
pet = Field(Pet)
@ -532,6 +568,7 @@ class TestDjangoObjectType:
class Meta:
model = PetModel
convert_choices_to_enum = []
fields = "__all__"
class Query(ObjectType):
pet = Field(Pet)

View File

@ -220,6 +220,16 @@ class DjangoObjectType(ObjectType):
% type(exclude).__name__
)
if fields is None and exclude is None:
warnings.warn(
"Creating a DjangoObjectType without either the `fields` "
"or the `exclude` option is deprecated. Add an explicit `fields "
"= '__all__'` option on DjangoObjectType {class_name} to use all "
"fields".format(class_name=cls.__name__,),
DeprecationWarning,
stacklevel=2,
)
django_fields = yank_fields_from_attrs(
construct_fields(model, registry, fields, exclude, convert_choices_to_enum),
_as=Field,