如何在 django rest 框架的分页 URL 中更改下一个键中的主机?

Posted

技术标签:

【中文标题】如何在 django rest 框架的分页 URL 中更改下一个键中的主机?【英文标题】:How to change the host in next key in a paginated URL in django rest framework? 【发布时间】:2020-10-06 20:15:48 【问题描述】:

我在 Django Rest Framework 中有一个带有分页响应的 ModelSerializer。所以我用 gunicorn 将它部署在一个 docker 容器中。

gunicorn -c gunicorn_config.py app.wsgi --bind 0.0.0.0:5000

现在问题在于分页响应。 next 键类似于。

next: "http://0.0.0.0:5000/admin/users/?page=2&per_page=10"

在我使用这些 API 的客户端中,我只需检查 next 键并获取下一个响应。但由于next 键的主机为0.0.0.0:5000,因此会导致API 调用失败。并且next 键不具备此目的。

所以目前,我的 API 服务器在一个单独的 docker 容器中运行。这是通过nginx中的反向代理设置的。

【问题讨论】:

@AkshatZala 这是一个 Django 问题,而不是烧瓶 @AkshatZala 端口不是问题,您也可以在任何您想要的端口上运行它。请阅读有关 gunicorn 的内容。 这能回答你的问题吗? Gunicorn, no module named 'myproject @AkshatZala 不相关,谢谢 请在您的帖子中添加标签 gunicorn 【参考方案1】:

所以我做了一个自定义分页类扩展PageNumberPagination

from rest_framework.pagination import PageNumberPagination
def replace_query_param(url, key, val):
    """
    Given a URL and a key/val pair, set or replace an item in the query
    parameters of the URL, and return the new URL.
    """
    (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
    scheme = "https"
    netloc = "api.example.com"
    query_dict = parse.parse_qs(query, keep_blank_values=True)
    query_dict[force_str(key)] = [force_str(val)]
    query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
    return parse.urlunsplit((scheme, netloc, path, query, fragment))


def remove_query_param(url, key):
    """
    Given a URL and a key/val pair, remove an item in the query
    parameters of the URL, and return the new URL.
    """
    (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
    scheme = "https"
    netloc = "api.example.com"
    query_dict = parse.parse_qs(query, keep_blank_values=True)
    query_dict.pop(key, None)
    query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
    return parse.urlunsplit((scheme, netloc, path, query, fragment))

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'per_page'
    max_page_size = 1000

    def get_next_link(self):
        if not self.page.has_next():
            return None
        url = self.request.build_absolute_uri()
        page_number = self.page.next_page_number()
        return replace_query_param(url, self.page_query_param, page_number)

    def get_previous_link(self):
        if not self.page.has_previous():
            return None
        url = self.request.build_absolute_uri()
        page_number = self.page.previous_page_number()
        if page_number == 1:
            return remove_query_param(url, self.page_query_param)
        return replace_query_param(url, self.page_query_param, page_number)

现在我在我所有的ViewSets 中都使用这个分页类

class TestViewSet(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAuthenticated]

    queryset = Test.objects.all().order_by("pk")
    serializer_class = test_serializers.TestSerializer
    pagination_class = LargeResultsSetPagination
    search_fields = ['name', 'description', 'follow_up', 'follow_up_type']
    filter_backends = (filters.SearchFilter,)

它完成了工作,最初的灵感https://***.com/a/62422235/5884045

【讨论】:

【参考方案2】:

DRF 分页器中的next 链接是使用请求中的主机名生成的。这是在请求中确定主机名的方式:

def _get_raw_host(self):
    """
    Return the HTTP host using the environment or request headers. Skip
    allowed hosts protection, so may return an insecure host.
    """
    # We try three options, in order of decreasing preference.
    if settings.USE_X_FORWARDED_HOST and (
            'HTTP_X_FORWARDED_HOST' in self.META):
        host = self.META['HTTP_X_FORWARDED_HOST']
    elif 'HTTP_HOST' in self.META:
        host = self.META['HTTP_HOST']
    else:
        # Reconstruct the host using the algorithm from PEP 333.
        host = self.META['SERVER_NAME']
        server_port = self.get_port()
        if server_port != ('443' if self.is_secure() else '80'):
            host = '%s:%s' % (host, server_port)
    return host

因此,请检查 HTTP_X_FORWARDED_HOST 标头是否设置了您需要的正确主机名,如果是,请在您的设置中将 USE_X_FORWARDED_HOST 设置为 True。还要确保将您需要的主机名添加到ALLOWED_HOSTS

您还可以覆盖PageNumberPagination 类中的get_next_link() 方法以提供所需的主机/域名

【讨论】:

好吧,这并没有解决我的问题,但是在从最重要的事情中得到启发后,我定制了自己的分页类。并解决了这个问题。一会儿分享解决方法。非常感谢,@Ken4scgolars

以上是关于如何在 django rest 框架的分页 URL 中更改下一个键中的主机?的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest 框架 - APIView 分页

Django框架实现的分页demo示例

Django:如何获取当前 URL 标记名(用于分页)?

Django REST框架分页的jQuery代码

django网页的分页功能,大家如果有疑问请留言

如何在Django项目中组织大量页面的分页?