I have the following Python module that implements Graphene.
JavaScript
x
101
101
1
import graphene
2
import json
3
import psycopg2
4
5
connection = psycopg2.connect(user='postgres', password='xxxxx', host='127.0.0.1', port='5432', database='xxxx')
6
cursor = connection.cursor()
7
8
class Entity(graphene.ObjectType):
9
name = graphene.String()
10
11
class Query(graphene.ObjectType):
12
entity_relationships = graphene.List(Entity, entity=graphene.String())
13
postgresql_version = graphene.String
14
path = graphene.String(name=graphene.String(), path=graphene.String(), accumulate=graphene.String(), depth=graphene.Int())
15
16
def get_foreign_relationships(self, entity):
17
cursor.execute('''
18
SELECT
19
tc.table_schema, tc.constraint_name, tc.table_name, kcu.column_name, ccu.table_schema AS foreign_table_schema, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name
20
FROM information_schema.table_constraints AS tc
21
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
22
AND tc.table_schema = kcu.table_schema
23
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
24
AND ccu.table_schema = tc.table_schema
25
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name='{}';'''.format(entity))
26
result = cursor.fetchall()
27
result_array = []
28
29
for record in result:
30
new_entity = Entity(name=record[5])
31
result_array.append(new_entity)
32
33
return result_array
34
35
def is_relationship(self, referencing, referenced):
36
foreign_relationships = self.get_foreign_relationships(referencing)
37
38
if referenced in list(map(lambda table: relationship[5], foreign_relationships)):
39
return True
40
else:
41
return False
42
43
def traverse(self, entities, direction):
44
for i in range(len(entities)):
45
if i > 0 and i < len(entities)-1:
46
if not self.is_relationship(entities[i], entities[i-1]):
47
raise "entity {} has no relation with entity {}".format(entities[i], entities[i-1])
48
49
return True
50
51
def validate_path(self, path):
52
entities = path.split('/')
53
54
self.traverse(entities)
55
56
return True
57
58
def resolve_path(self, info, name, path, accumulate, depth):
59
if self.validate_path(path):
60
return "Éste método regresará los datos anidados para el camino '{}'".format(path)
61
62
def resolve_entity_relationships(self, info, entity):
63
result_array = self.get_foreign_relationships(entity)
64
65
return result_array
66
67
def resolve_postgresql_version(self, info):
68
cursor.execute("SELECT version();")
69
record = cursor.fetchone()
70
return record
71
72
def execute_query(query_to_execute):
73
queries = {
74
'postgresqlVersion': '''
75
{
76
postgresqlVersion
77
}
78
''',
79
'entityRelationships': '''
80
{
81
entityRelationships (entity: "inventory_productitem") {
82
name
83
}
84
}
85
''',
86
'path': '''
87
{
88
path(name: "Ventas", path: "inventory_family/inventory_product/inventory_sale", accumulate: "_calculate(amount*price)", depth: 1)
89
}
90
'''
91
}
92
93
schema = graphene.Schema(query=Query)
94
95
result = schema.execute(queries[query_to_execute])
96
97
dict_result = dict(result.data.items())
98
print(json.dumps(dict_result, indent=2))
99
100
execute_query('path')
101
The problem is that resolvers that call other methods of the class raise this error:
JavaScript
1
9
1
Traceback (most recent call last):
2
File "/Users/hugovillalobos/Documents/Code/agrowareproject/backend/AgrowareVenv/lib/python3.7/site-packages/graphql/execution/executor.py", line 452, in resolve_or_error
3
return executor.execute(resolve_fn, source, info, **args)
4
File "/Users/hugovillalobos/Documents/Code/agrowareproject/backend/AgrowareVenv/lib/python3.7/site-packages/graphql/execution/executors/sync.py", line 16, in execute
5
return fn(*args, **kwargs)
6
File "query.py", line 59, in resolve_path
7
if self.validate_path(path):
8
graphql.error.located_error.GraphQLLocatedError: 'NoneType' object has no attribute 'validate_path'
9
The line that rises the error is:
if self.validate_path(path):
I don’t know why, if method validate_path()
is in the same class that the method that calls it.
Advertisement
Answer
This is because Graphene considering all resolver methods are staticmethods
From the doc,
Sometimes this argument will be named
self
in Graphene code, but this can be misleading due to Implicit staticmethod while executing queries in Graphene.
So, you must rearrange your code as follows,
JavaScript
1
18
18
1
def validate_path(path):
2
# do something
3
return True # ot False depends on your evaluation
4
5
6
class Query(graphene.ObjectType):
7
path = graphene.String(
8
name=graphene.String(),
9
path=graphene.String(),
10
accumulate=graphene.String(),
11
depth=graphene.Int()
12
)
13
14
@staticmethod
15
def resolve_path(parent, info, name, path, accumulate, depth):
16
if validate_path(path):
17
return "Validated path"
18
return "Path Not Valid"