Django-rest-framework: IndexError para CursorPagination quando o queryset muda

Criado em 11 mar. 2019  ·  4Comentários  ·  Fonte: encode/django-rest-framework

Lista de controle

  • [X] Eu verifiquei que esse problema existe no ramo master da estrutura REST do Django.
  • [x] Pesquisei problemas semelhantes em tickets abertos e fechados e não consigo encontrar uma duplicata.
  • [x] Esta não é uma questão de uso. (Em vez disso, eles devem ser direcionados ao grupo de discussão .)
  • [x] Isso não pode ser tratado como uma biblioteca de terceiros. (Preferimos que a nova funcionalidade esteja na forma de bibliotecas de terceiros, sempre que possível.)
  • [x] Reduzi o problema ao caso mais simples possível.
  • [x] Incluí um teste de falha como uma solicitação de pull. (Se você não puder fazer isso, ainda podemos aceitar o problema.)

Passos para reproduzir

  1. Habilitar CursorPagination
  2. Faça itens suficientes para que haja paginação
  3. Vá para a página 2
  4. Exclua todos os itens dentro do shell
  5. Recarregue a página
  6. Ver erro

Comportamento esperado

Para ser honesto, não tenho certeza, talvez uma página em branco.

Comportamento real

Traceback (most recent call last):
  File "/var/env/lib/python3.6/site-packages/django/contrib/staticfiles/handlers.py", line 65, in __call__
    return self.application(environ, start_response)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/wsgi.py", line 142, in __call__
    response = self.get_response(request)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 78, in get_response
    response = self._middleware_chain(request)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 36, in inner
    response = response_for_exception(request, exc)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
    response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 125, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
  File "/var/env/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 37, in null_technical_500_response
    six.reraise(exc_type, exc_value, tb)
  File "/var/env/lib/python3.6/site-packages/six.py", line 692, in reraise
    raise value.with_traceback(tb)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/var/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/var/env/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/var/env/lib/python3.6/site-packages/rest_framework/viewsets.py", line 116, in view
    return self.dispatch(request, *args, **kwargs)
  File "/var/env/lib/python3.6/site-packages/rest_framework/views.py", line 495, in dispatch
    response = self.handle_exception(exc)
  File "/var/env/lib/python3.6/site-packages/rest_framework/views.py", line 455, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/var/env/lib/python3.6/site-packages/rest_framework/views.py", line 492, in dispatch
    response = handler(request, *args, **kwargs)
  File "/var/env/lib/python3.6/site-packages/rest_framework/mixins.py", line 45, in list
    return self.get_paginated_response(serializer.data)
  File "/var/env/lib/python3.6/site-packages/rest_framework/generics.py", line 180, in get_paginated_response
    return self.paginator.get_paginated_response(data)
  File "/var/env/lib/python3.6/site-packages/rest_framework/pagination.py", line 781, in get_paginated_response
    ('previous', self.get_previous_link()),
  File "/var/env/lib/python3.6/site-packages/rest_framework/pagination.py", line 643, in get_previous_link
    compare = self._get_position_from_instance(self.page[0], self.ordering)
IndexError: list index out of range

Comentários muito úteis

Obrigado @ewjoachim e @tomchristie 👍

Todos 4 comentários

Vou tentar tentar. Se não houver notícias minhas até 15 de abril, considere que não estou mais nele.

Só para constar, não posso repetir ao seguir as etapas exatas do tíquete, mas se tento voltar à página 1, obtenho o mesmo traceback.

Adicionando algumas informações: a paginação do cursor pode ter 3 parâmetros: posição, deslocamento e cursor. O bug é acionado quando:

  • Você está em uma página que não contém mais objetos, porque eles foram excluídos (isso funciona e retorna uma lista vazia)
  • O link para a página anterior, mesmo depois de recarregar, inclui um cursor com deslocamento e sem posição.
  • Seguir este link leva a um 500

Isso ocorre porque o atributo self.page está vazio, nenhum elemento "exclusivo" é assumido como estando na página, a página é considerada como contendo apenas um elemento que tem a mesma posição e, portanto, o algoritmo resolve usar um deslocamento pedido baseado em

O seguinte comentário:

https://github.com/encode/django-rest-framework/blob/29cbe574a384c3bcc09434a3a9c5ff0cb7576b99/rest_framework/pagination.py#L615

... engloba o erro. Essa suposição está errada se a página estiver vazia.

Corrigir a lógica e dizer ao algoritmo para verificar se a página está vazia e então definir a posição para previous_position / next_position é o suficiente para se livrar deste bug.

PR virá em seguida.

Obrigado @ewjoachim e @tomchristie 👍

Esta página foi útil?
0 / 5 - 0 avaliações