graphql-python / graphql-python/graphene-django
Multi-/Cross-fields-permission-check for `DjangoObjectType`
- Dominant language
- Python
- Stars
- 4.4k
- Forks
- 760
- PR merge metrics
- No merged PRs in 30d
Description
## The Challenge
I have models where I want to restrict the content of some fields based on the relationship between the user and the current instance of that model.
The challenge is that if I write custom field resolvers for each field that I want to alter, I end up doing a db query for each such field, which ends up becoming really slow if I get a whole QuerySet of the node type.
How can I prevent doing a query for each and every field resolution?
**Example**
If I have a model `Book` and each book has an `author` field which is a FK to the user model.
Then on the `BookNode` I have the field-level checks:
```python
def resolve_authors_only_field1(self, info):
user = info.context.user
if user in self.author_set.all():
return self.authors_only_field1
return "some default" # In case of a required field type
def resolve_authors_only_field2(self, info):
user = info.context.user
if user in self.author_set.all():
return self.authors_only_field2
return None # In case of an optional field type
```
**Performance Bottleneck**
This does work, but as far as I understand it will do a db query for executing every field resolver!
Meaning: If I get 20 books and I have 20 restricted/conditional fields, I end up doing 400 db queries. :neutral_face:
## Solution Ideas
**Mental Inspiration**
FYI: As an inspiration I was also looking at similar multi-/cross-field challenges.
I also looked at how Django Form validation tries to allow something similar.
Single field validation:
```python
def validate_myfield(self, value):
...
```
Cross-field validation:
```python
def validate(self, data):
...
```
### Solution idea 1: Cross-field resolver hook
Being able to change the fields based on a hook after individual fields have been resolved:
Single field resolver hook:
```python
def resolve_myfield(self, info):
...
```
Cross-field resolver hook:
```python
def resolve_fields(self, info, data):
...
```
### Solution idea 2: Dynamic fields/only-fields/exclude-fields
Make it possible to change the fields/only-fields/exclude-fields attributes at runtime. (e.g. when starting to resolve the node?)
Or allow fields/only-fields/exclude-fields to accept a function that will be called when resolving the node.
**Update**: @jkimbo pointed out that it’s not a good idea to change the types at runtime and that the schema should be static.
### Alternative Ideas
Changing the type dynamically when resolving: https://github.com/graphql-python/graphene-django/issues/79#issuecomment-295122545
(Nice idea, but might end up adding more complexity and types than necessary.)
Overwriting the resolver entirely: https://github.com/graphql-python/graphene-django/issues/79#issuecomment-599307608
## Additional context
```
graphene 2.1.8
graphene-django 2.11.0
```
If there are any questions to better explain my scenario/user-cases/etc.
Please feel free to ask and I will do my best to explain. :)
## Comments
I really wonder if there is a smarter / more intuitive way of rewriting my code to not do a query per field. 🤔
Contributor guide
Assessment
This issue has not been assessed yet.