簡體   English   中英

如何在 django rest 框架中的分頁 URL 中更改下一個鍵中的主機?

[英]How to change the host in next key in a paginated URL in django rest framework?

我在 Django Rest 框架中有一個帶有分頁響應的 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中的反向代理設置的。

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 header 是否設置了您需要的正確主機名,如果是,請在您的設置中將USE_X_FORWARDED_HOST設置為True 還要確保將您需要的主機名添加到ALLOWED_HOSTS中。

您還可以覆蓋PageNumberPagination class 中的get_next_link()方法以提供所需的主機/域名

所以我做了一個自定義分頁 class 擴展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://stackoverflow.com/a/62422235/5884045

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM