问题描述
我有一个Django项目,正在使用django-graphene创建GraphQL API。
尝试将DjangoFilterConnectionField
与relay.Connection
(which is the core of pagination's feature)一起使用时出现问题
我的模型太大,有很多关系,但是让我们保持简单...
class Pattern(models.Model):
code = models.CharField(
max_length=15
)
name = models.CharField(
max_length=50
)
slug = AutoSlugField(
populate_from='name',max_length=150
)
...
我的节点如下:
class PatternNode(DjangoObjectType):
# Many fields here...
...
class Meta:
model = Pattern
interfaces = (relay.Node,)
filterset_class = PatternFilterSet
如您所见,我已经在节点的filterset_class
中设置了Meta
属性。
因此,这是该过滤器集:
class PatternFilterSet(FilterSet):
order_by = OrderingFilter(
fields=(
('date','date'),('name','name'),)
)
productcategorization__design__contains = CharFilter(method="product_categorization_design_filter")
products__predominant_colors__contains = CharFilter(method="products_predominant_colors_filter")
class Meta:
model = Pattern
fields = {
'name': ['exact','icontains','istartswith'],'alt_name': ['exact','slug': ['exact'],'pattern_class': ['exact'],'sectors': ['exact','in'],'products__instances': ['exact'],'productcategorization__business': ['exact'],'productcategorization__market_segment': ['exact',}
@staticmethod
def product_categorization_design_filter(queryset,name,value):
"""
Does a productcategorization__design__contains filter "manually" because adding it in the Meta.fields does not
work for ArrayField.
Args:
queryset (patterns.managers.PatternQuerySet)
name (str)
value (Array) comma delimited list of designs
Returns:
filtered_queryset (QuerySet)
"""
return queryset.filter(productcategorization__design__contains=value.split(","))
@staticmethod
def products_predominant_colors_filter(queryset,value):
"""
Does a products__predominant_colors__contains filter "manually" because adding it in the Meta.fields does not
work for ArrayField.
Args:
queryset (patterns.managers.PatternQuerySet)
name (str)
value (Array) comma delimited list of designs
Returns:
filtered_queryset (QuerySet)
"""
return queryset.filter(products__predominant_colors__contains=value.split(",")).distinct()
如您所见,我的API中需要针对该特定模型的许多特殊过滤选项。
class PatternConnection(relay.Connection):
class Meta:
node = PatternNode
class Query(graphene.ObjectType):
pattern = relay.Node.Field(
PatternNode,id=ID(),slug=String()
)
patterns = relay.ConnectionField(PatternConnection)
目前一切正常,但过滤器不起作用。
我正在执行以下查询:
query Patterns {
patterns(first: 2) {
pageInfo {
startCursor
endCursor
hasNextPage
}
edges {
cursor
node {
id
name
}
}
}
}
,并收到以下回复:
{
"data": {
"patterns": {
"pageInfo": {
"startCursor": "YXJyYXljb25uZWN0aW9uOjA=","endCursor": "YXJyYXljb25uZWN0aW9uOjE=","hasNextPage": true
},"edges": [
{
"cursor": "YXJyYXljb25uZWN0aW9uOjA=","node": {
"id": "UGF0dGVybk5vZGU6Mjcw","name": "42 Oz - Jk"
}
},{
"cursor": "YXJyYXljb25uZWN0aW9uOjE=","node": {
"id": "UGF0dGVybk5vZGU6Mjcx","name": "42 Oz - Pebble Top - Jk"
}
}
]
}
}
}
现在,当我使用自己的过滤器之一进行尝试时,就像这样:
query Patterns ($predominantColors: String) {
patterns(first: 2,products_PredominantColors_Contains: $predominantColors) {
pageInfo {
startCursor
endCursor
hasNextPage
}
edges {
cursor
node {
id
name
}
}
}
}
我收到以下答复:
{
"errors": [
{
"message": "UnkNown argument \"products_PredominantColors_Contains\" on field \"patterns\" of type \"Query\".","locations": [
{
"line": 2,"column": 24
}
]
}
]
}
我认为是因为我没有使用DjangoFilterConnectionField
as suggested here,但是当我尝试这样做时:
class PatternConnection(relay.Connection):
class Meta:
node = PatternNode
class Query(graphene.ObjectType):
pattern = relay.Node.Field(
PatternNode,slug=String()
)
patterns = DjangoFilterConnectionField(PatternConnection)
我遇到以下错误:
September 23,2020 - 17:06:12
Django version 2.2.12,using settings 'proquinal_api.settings'
Starting development server at http://api.spradling.local:8000/
Quit the server with CONTROL-C.
/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/types.py:131: UserWarning: Django model "cities_light.City" does not have a field or attribute named "location". Consider removing the field from the "exclude" list of DjangoObjectType "CityNode" because it has no effect
type_=type_,Internal Server Error: /graphql/
Traceback (most recent call last):
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/settings.py",line 79,in import_from_string
module = importlib.import_module(module_path)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/python3.framework/Versions/3.7/lib/python3.7/importlib/__init__.py",line 127,in import_module
return _bootstrap._gcd_import(name[level:],package,level)
File "<frozen importlib._bootstrap>",line 1006,in _gcd_import
File "<frozen importlib._bootstrap>",line 983,in _find_and_load
File "<frozen importlib._bootstrap>",line 967,in _find_and_load_unlocked
File "<frozen importlib._bootstrap>",line 677,in _load_unlocked
File "<frozen importlib._bootstrap_external>",line 728,in exec_module
File "<frozen importlib._bootstrap>",line 219,in _call_with_frames_removed
File "/Users/cristianrojas/www/spradling-api/proquinal_api/schema.py",line 49,in <module>
mutation=Mutation
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene/types/schema.py",line 78,in __init__
self.build_typemap()
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene/types/schema.py",line 168,in build_typemap
initial_types,auto_camelcase=self.auto_camelcase,schema=self
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene/types/typemap.py",line 80,in __init__
super(TypeMap,self).__init__(types)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphql/type/typemap.py",line 31,in __init__
self.update(reduce(self.reducer,types,OrderedDict())) # type: ignore
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene/types/typemap.py",line 88,in reducer
return self.graphene_reducer(map,type)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene/types/typemap.py",line 117,in graphene_reducer
return GraphQLTypeMap.reducer(map,internal_type)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphql/type/typemap.py",line 109,in reducer
field_map = type_.fields
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphql/pyutils/cached_property.py",line 22,in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphql/type/deFinition.py",line 198,in fields
return define_field_map(self,self._fields)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphql/type/deFinition.py",line 212,in define_field_map
field_map = field_map()
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene/types/typemap.py",line 275,in construct_fields_for_type
map = self.reducer(map,field.type)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/fields.py",line 98,in type
assert _type._Meta.connection,"The type {} doesn't have a connection".format(
AttributeError: 'Connectionoptions' object has no attribute 'connection'
During handling of the above exception,another exception occurred:
Traceback (most recent call last):
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/django/core/handlers/exception.py",line 34,in inner
response = get_response(request)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/django/core/handlers/base.py",line 115,in _get_response
response = self.process_exception_by_middleware(e,request)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/django/core/handlers/base.py",line 113,in _get_response
response = wrapped_callback(request,*callback_args,**callback_kwargs)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/django/views/decorators/csrf.py",line 54,in wrapped_view
return view_func(*args,**kwargs)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/django/views/generic/base.py",line 62,in view
self = cls(**initkwargs)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/views.py",line 100,in __init__
schema = graphene_settings.SCHEMA
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/settings.py",line 126,in __getattr__
val = perform_import(val,attr)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/settings.py",line 65,in perform_import
return import_from_string(val,setting_name)
File "/Users/cristianrojas/.virtualenvs/spradling-api-YJ5S1R6Y/lib/python3.7/site-packages/graphene_django/settings.py",in import_from_string
raise ImportError(msg)
ImportError: Could not import 'proquinal_api.schema.schema' for Graphene setting 'SCHEMA'. AttributeError: 'Connectionoptions' object has no attribute 'connection'.
[23/Sep/2020 17:08:02] "POST /graphql/ HTTP/1.1" 500 212017
因此,我想知道将DjangoFilterConnectionField
与PatternConnection
中继的连接结合使用以使过滤器和分页协同工作的正确方法是什么。
解决方法
以
的方式将 PatternNode
传递到 DjangoFilterConnectionField
import graphene
class PatternNode(DjangoObjectType):
# Many fields here...
...
class Meta:
model = Pattern
interfaces = (relay.Node,)
filterset_class = PatternFilterSet
class Query(graphene.ObjectType):
patterns = DjangoFilterConnectionField(PatternNode)