在基于 Django 的 GraphQL API 中为 postgres 表创建全局搜索字段的最佳方法?

Posted

技术标签:

【中文标题】在基于 Django 的 GraphQL API 中为 postgres 表创建全局搜索字段的最佳方法?【英文标题】:Best approach to create a global search field for a postgres table in Django based GraphQL API? 【发布时间】:2021-08-13 19:14:57 【问题描述】:

我正在使用带有 Django-graphene GraphQL API 和 postgres db 的 Angular UI。

目前,我已经实现了一个功能,通过为每个表添加一个“searchField”并在每次创建和更新该表中的项目时更新它来到达全局搜索字段。并且使用 graphql 过滤器,每次用户进行全局搜索时,我都会为查询过滤 hte searchField。我对 Django 很陌生,所以我不确定这是否是解决此问题的有效方法,但这就是我所拥有的:-

创建突变

class CreateUser(graphene.Mutation):
    class Arguments:
        input = UserInput(required=True)

    ok = graphene.Boolean()
    user = graphene.Field(UserType)

    @staticmethod
    def mutate(root, info, input=None):
        ok = True
        searchField = input.username if input.username is not None else "" + \
            input.title if input.title is not None else "" + \
            input.bio if input.bio is not None else ""
        user_instance = User(user_id=input.user_id, title=input.title, bio=input.bio,
                             institution_id=input.institution_id, searchField=searchField)
        user_instance.save()
        return CreateUser(ok=ok, user=user_instance)

更新变异

class UpdateUser(graphene.Mutation):
    class Arguments:
        id = graphene.Int(required=True)
        input = UserInput(required=True)

    ok = graphene.Boolean()
    user = graphene.Field(UserType)

    @staticmethod
    def mutate(root, info, id, input=None):
        ok = False
        user = User.objects.get(pk=id)
        user_instance = user
        if user_instance:
            ok = True
            user_instance.name = input.name if input.name is not None else user.name
            user_instance.avatar = input.avatar if input.avatar is not None else user.avatar
            user_instance.institution_id = input.institution_id if input.institution_id is not None else user.institution_id
            user_instance.title = input.title if input.title is not None else user.title
            user_instance.bio = input.bio if input.bio is not None else user.bio
            user_instance.searchField = user_instance.searchField + \
                user_instance.name if user_instance.name is not None else ""
            user_instance.searchField = user_instance.searchField + \
                user_instance.title if user_instance.title is not None else ""
            user_instance.searchField = user_instance.searchField + \
                user_instance.bio if user_instance.bio is not None else ""
            user_instance.save()
            return UpdateUser(ok=ok, user=user_instance)
        return UpdateUser(ok=ok, user=None)

不确定你是否能说出来,但我对 Python 和 Django 很陌生。我在这里所做的是,每次创建或更新用户记录时,我都会在表中维护一个名为 searchField 的字段,该字段将包含表中我希望全局搜索的所有字段的字符串涉及。我已经像这样手动编写了每一行。不确定这是否符合最佳做法。

这是用户模型。

用户模型

class User(AbstractUser):
    name = models.CharField(max_length=50)
    email = models.EmailField(blank=False, max_length=255, unique=True)
    avatar = models.CharField(max_length=250, blank=True, null=True)
    institution = models.ForeignKey(
        'Institution', on_delete=models.PROTECT, blank=True, null=True)
    title = models.CharField(max_length=150, blank=True, null=True)
    bio = models.CharField(max_length=300, blank=True, null=True)
    searchField = models.CharField(max_length=600, blank=True, null=True)
    USERNAME_FIELD = 'username'
    EMAIL_FIELD = 'email'

所以这里有几件事。

    首先,我知道我在设置更新突变解析器方法中的 searchField 更新方面做错了。那是由于我对Python的了解不足。因此,如果有人能弄清楚我做错了什么,那就太好了。 我完全是在自己做这件事,我不知道这种方法在效率方面是否真的是一个好的策略,或者是否已经有一个基于 graphql 的 django api 的好的解决方案。所以如果有请指点我。

谢谢。

【问题讨论】:

【参考方案1】:

是的,如果您想更新您的 searchField,您必须从头开始构建它。如果您使用旧数据并附加新数据,如果您搜索旧数据,它将返回记录,这是您要避免的。

关于第二个问题,您可以使用django-filter

步骤:

    安装django-filter

    创建一个基本过滤器集类并向其添加search 字段

    class CustomFilterSet(filters.FilterSet):
        search = filters.CharFilter(method='search_filter')
    
        def search_filter(self, queryset, field_name, value):
            return queryset
    

    为继承自自定义过滤器集类的模型创建过滤器集类并覆盖search_filter 方法。为了对查询集执行or 操作,请使用Q 对象。在你的情况下是这样的:

    class UserFilterSet(CustomFilterSet):
        ...
        def search_filter(self, queryset, field_name, value):
            return queryset.filter(
                Q(username__icontains=value) |
                Q(title__icontains=vallue) |
                Q(bio__icontains=value)
            )
    

    在指定字段中查找值,而不管 小写/大写

    创建graphql类型并将相关的filterset_class添加到Meta类:

    class UserType(DjangoObjectType):
        class Meta:
            model = User
            filterset_class = UserFilterSet
            interfaces = (relay.Node, )
    

现在您可以通过仅传递一个字符串来全局搜索指定的字段。

【讨论】:

以上是关于在基于 Django 的 GraphQL API 中为 postgres 表创建全局搜索字段的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

Django 中的 GraphQL 查询返回 None

GraphQL graphene-django 基本使用文档

当我使用 Graphene 在 Django GraphQL API 中获取对象时,如何限制 ForeignKey 字段的项目数?

如何使用 Django 和 Neo4j 数据库创建 GraphQL API?

基于类的视图django中的JWT验证

公开基于 GraphQL 的 API