graphql-python / graphql-python/graphene-django

Multi-/Cross-fields-permission-check for `DjangoObjectType`

Open
#997 0 comments 0 reactions 0 assignees View on GitHub
✨enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.