如何仅向graphene-django中的用户个人资料所有者显示特定字段?

Posted

技术标签:

【中文标题】如何仅向graphene-django中的用户个人资料所有者显示特定字段?【英文标题】:How to show specific field only to the user profile owner in graphene-django? 【发布时间】:2020-02-07 21:11:12 【问题描述】:

我的graphene-django 应用程序中有以下架构:

import graphene
from django.contrib.auth import get_user_model
from graphene_django import DjangoObjectType


class UserType(DjangoObjectType):
    class Meta:
        model = get_user_model()
        fields = ("id", "username", "email")


class Query(object):
    user = graphene.Field(UserType, user_id=graphene.Int())

    def resolve_user(self, info, user_id):
        user = get_user_model().objects.get(pk=user_id)
        if info.context.user.id != user_id:
            # If the query didn't access email field -> query is ok
            # If the query tried to access email field -> raise an error
        else:
            # Logged in as the user we're querying -> let the query access all the fields

我希望能够通过以下方式查询架构:

# Logged in as user 1 => no errors, because we're allowed to see all fields
query 
  user (userId: 1) 
    id
    username
    email
  


# Not logged in as user 1 => no errors, because not trying to see email
query 
  user (userId: 1) 
    id
    username
  


# Not logged in as user 1 => return error because accessing email
query 
  user (userId: 1) 
    id
    username
    email
  

我怎样才能做到只有登录用户才能看到自己个人资料的email字段,而其他人不能看到其他人的电子邮件?

【问题讨论】:

看一下石墨烯中间件的authorization example。 @TomasLinhart 我已经经历过很多次了,但我不知道该怎么做。 你说得对,在这种情况下使用授权中间件可能有点多余。但是,解决方案取决于您是要返回没有email 字段的用户对象(由第一个代码块建议)还是引发错误(由第二个代码块建议)。如果是前者,只需在if info.context.user.id != user_id: 分支中执行user['email'] = Nonedel user['email']。如果是后者,请在此处引发异常。 @TomášLinhart 我稍微编辑了我的 graphql 和 python cmets。如果查询正在访问电子邮件并且未登录到用户,我只想引发错误。您介意将您的评论扩展到答案吗? 【参考方案1】:

这是我将根据 cmets 采取的方法。这里的主要问题是能够获取解析器中查询请求的字段列表。为此,我使用了改编自 here 的代码:

def get_requested_fields(info):
    """Get list of fields requested in a query."""
    fragments = info.fragments

    def iterate_field_names(prefix, field):
        name = field.name.value
        if isinstance(field, FragmentSpread):
            results = []
            new_prefix = prefix
            sub_selection = fragments[name].selection_set.selections
        else:
            results = [prefix + name]
            new_prefix = prefix + name + '.'
            sub_selection = \
                field.selection_set.selections if field.selection_set else []
        for sub_field in sub_selection:
            results += iterate_field_names(new_prefix, sub_field)
        return results

    results = iterate_field_names('', info.field_asts[0])
    return results

其余的应该很简单:

import graphene
from django.contrib.auth import get_user_model
from graphene_django import DjangoObjectType


class AuthorizationError(Exception):
    """Authorization failed."""


class UserType(DjangoObjectType):
    class Meta:
        model = get_user_model()
        fields = ("id", "username", "email")


class Query(object):
    user = graphene.Field(UserType, user_id=graphene.Int())

    def resolve_user(self, info, user_id):
        user = get_user_model().objects.get(pk=user_id)
        if info.context.user.id != user_id:
            fields = get_requested_fields(info)
            if 'user.email' in fields:
                raise AuthorizationError('Not authorized to access user email')
        return user

【讨论】:

【参考方案2】:

目前的答案过于复杂。只需创建两个 ObjectType,例如:

class PublicUserType(DjangoObjectType):
    class Meta:
        model = get_user_model()
        fields  = ('id', 'username')

class PrivateUserType(DjangoObjectType):
    class Meta:
        model = get_user_model()

花了 4 个多小时尝试其他解决方案,然后才意识到原来如此简单

【讨论】:

【参考方案3】:

我最后就是这样做的,查询自己的信息时返回email的实际值,为别人返回None

import graphene
from django.contrib.auth import get_user_model
from graphene_django import DjangoObjectType


class UserType(DjangoObjectType):
    class Meta:
        model = get_user_model()
        fields = ("id", "username", "email")

    def resolve_email(self, info):
        if info.context.user.is_authenticated and self.pk == info.context.user.pk:
            return self.email
        else:
            return None


class Query(graphene.ObjectType):
    user = graphene.Field(UserType, user_id=graphene.Int())

    def resolve_user(self, info, user_id):
        return get_user_model().objects.get(pk=user_id)

【讨论】:

以上是关于如何仅向graphene-django中的用户个人资料所有者显示特定字段?的主要内容,如果未能解决你的问题,请参考以下文章

仅向 Google 网站登录请求“openid”范围

仅向firebase中的一个用户发送通知[关闭]

仅向某些选定用户显示我在应用商店中的应用

graphene-django 中的自定义参数

Android firebase:仅向付费用户发送推送通知

如何使用 Graphene-Django Relay 中的外键关系更新模型?