如何隐藏/排除graphene_django中请求实体的某些外键字段?
Posted
技术标签:
【中文标题】如何隐藏/排除graphene_django中请求实体的某些外键字段?【英文标题】:How to hide/exclude certain fields of foreign key in graphene_django wrt the requested entity? 【发布时间】:2020-12-09 05:44:06 【问题描述】:我已经阅读了如何在这些链接中排除(隐藏)django 和 graphene_django 中的某些字段:
graphql-python repository Why does my excluded field still appear in this Django form? 等假设我们有以下Post
模型,它具有User
模型的外键。
apps/posts/models.py
from django.db import models
from apps.users.models import User
class Post(models.Model):
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=False
)
created_at = models.DateTimeField(
auto_now_add=True,
)
title = models.TextField(
null=False,
max_length=100,
)
content = models.TextField(
null=False,
)
def __str__(self):
return self.title
apps/users/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser, models.Model):
phone_no = models.CharField(
blank=True,
null=True,
default="",
max_length=10,
verbose_name="Phone Number",
)
avatar = models.ImageField(
null=True,
upload_to='static',
)
USERNAME_FIELD = "username"
EMAIL_FIELD = "email"
def __str__(self):
return self.username
我尝试了以下方法,但没有按预期工作:
apps/posts/schema.py
import graphene
from graphene import Mutation, InputObjectType, ObjectType
from graphene_django.types import DjangoObjectType
from .models import Post
class PostType(DjangoObjectType):
class Meta:
model = Post
exclude_fields = [
'created_at', #it worked
'author.password', #the way I tried to hide the foreign key field
]
class Query(ObjectType):
posts = graphene.List(
PostType
)
def resolve_posts(self, info):
#TODO: pagination
return Post.objects.all()
截图:
如何在 graphql 模型类型中隐藏它的某些字段(如上例中的作者密码)?
【问题讨论】:
如何创建一个排除字段password
的AuthorType(DjangoObjectType)
并在PostType
和author=graphene.Field(AuthorType)
中使用此类型
@SagarAdhikari 你的意思是把它添加到 PostType 元类中?
否,在元类之上,(类似于类变量)。
@SagarAdhikari 你能给我举个例子吗?想象一下您根据不同的权限执行此操作的情况。例如,超级用户可以看到电子邮件字段,但普通用户不能。
GraphQL API 需要事先指定您可以从服务器获取/发布到服务器的数据类型。因此,据我所知,它违反了 graphql 标准。但是您显然可以检查权限并向普通用户发送消息“此字段不可访问”,而不是发送数据。
【参考方案1】:
从 cmets 了解到,您有一个 UserType
类
您可以在元数据中使用 exclude
选项,
class UserType(DjangoObjectType):
class Meta:
model = User
exclude = ('password',)
更新
您还可以使用自定义解析器来检查请求的实体
class UserType(DjangoObjectType):
password = graphene.String()
def resolve_password(self, info):
requested_user = info.context.user
if requested_user.email in ['admin@test.com', 'ceo@test.com']:
return self.password
return None
class Meta:
model = User
fields = '__all__'
【讨论】:
我不想为所有用户排除它。正如我在帖子中解释的那样,我想对具有不同权限的特定用户隐藏它。 哦..我明白了。 AFAIK,没有开箱即用的方法来做这种权限检查 所以你的意思是它有严重的安全问题?如果您能给我投票,我将不胜感激 我认为这不是安全问题“问题”。我认为这是 “此功能尚未在石墨烯中实现” 那么 Facebook、Instagram 和其他人是如何使用它的呢?你的意思是他们为此尝试了不同的API?喜欢restful api?【参考方案2】:这只是在解析查询字段之前检查权限的答案。 (所以,不是原始问题的答案)
这样的事情会起作用。
def permission_check_my_field(func):
@wraps(func)
def wrapper(self,info,**kwargs):
user=info.context.user
if (......) # permit condition here
return func(self, info,**kwargs)
else:
return None
return wrapper
class Query(graphene.ObjectType):
my_field = graphene.Field(...) # or graphene.List or .....
@permission_check_my_field
def resolve_my_field(......)
# do your normal work
更新。
如果用户数据足以检查该字段是否可访问(如other answer),则上述代码有效。但是,如果您需要检查用户是否已被授予访问该字段的某些权限,那么您需要这样做:
def permission_check_in_query(perm):
def wrapped_decorator(func):
@wraps(func)
def wrapper(self,info,**kwargs):
user=info.context.user
if user.has_perm(perm) # check if the user has privilege
return func(self, info,**kwargs)
else:
return None
# All fields are not nullable, so `return None' might throw error. You can pass `what you need to return` in decorator argument and use it here , to avoid this.
return wrapper
return wrapped_decorator
class Query(graphene.ObjectType):
my_field = graphene.Field(...) # or graphene.List or .....
@permission_check_in_query('model.access_my_field') # your permission code
# learn django permissions if you are not sure what it is
# doesn't have to be django_permission, can be any argument like 'is_it_a_superuser' that you will use to check user privilege. Modify decorator code accordingly
def resolve_my_field(......)
# do your normal work
这样做,您可以重复使用任何字段和任何权限。只需将装饰器 @permission_check_in_query(your arguments)
添加到需要在解析之前进行权限检查的任何字段上方即可。
TLDR:关于 API 接受和返回的数据类型,此答案类似于 other answer。它只是提供可重用性和权限检查。
【讨论】:
我已经为这个问题添加了赏金,如果你能指导我该怎么做并更新你的答案,我将不胜感激。 我给出的这个答案与另一个答案相似,除了它返回“PermissionDenied”并且我刚刚添加了可重用的装饰器。您可以创建一个接受参数的装饰器,即您的权限代码(这里我使用了无参数装饰器),这将使其可用于任何字段。 现在,我更新了带有参数的 docorator,您可以在其中通过权限和检查。 现在一切正常:)以上是关于如何隐藏/排除graphene_django中请求实体的某些外键字段?的主要内容,如果未能解决你的问题,请参考以下文章