Django-rest-framework: IndexError untuk CursorPagination saat queryset berubah

Dibuat pada 11 Mar 2019  ·  4Komentar  ·  Sumber: encode/django-rest-framework

Daftar periksa

  • [X] Saya telah memverifikasi bahwa masalah itu ada pada cabang master dari kerangka kerja Django REST.
  • [x] Saya telah mencari masalah serupa di tiket terbuka dan tertutup dan tidak dapat menemukan duplikat.
  • [x] Ini bukan pertanyaan penggunaan. (Itu harus diarahkan ke grup diskusi sebagai gantinya.)
  • [x] Ini tidak dapat ditangani sebagai perpustakaan pihak ketiga. (Kami lebih suka fungsionalitas baru dalam bentuk perpustakaan pihak ketiga jika memungkinkan.)
  • [x] Saya telah mengurangi masalah menjadi kasus yang paling sederhana.
  • [x] Saya telah menyertakan tes yang gagal sebagai permintaan tarik. (Jika Anda tidak dapat melakukannya, kami masih dapat menerima masalah tersebut.)

Langkah-langkah untuk mereproduksi

  1. Aktifkan CursorPagination
  2. Buat item yang cukup sehingga ada pagination
  3. Buka halaman 2
  4. Hapus semua item di dalam shell
  5. Segarkan halaman
  6. Lihat kesalahan

Perilaku yang diharapkan

Sejujurnya saya tidak yakin, mungkin halaman kosong.

Perilaku sebenarnya

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

Komentar yang paling membantu

Terima kasih @ewjoachim dan @tomchristie 👍

Semua 4 komentar

Saya akan mencoba dan mencobanya. Jika tidak ada berita dari saya sampai tanggal 15 April, anggap saya tidak ada di dalamnya lagi.

Sebagai catatan saya tidak dapat mereproduksi ketika mengikuti langkah-langkah yang tepat dari tiket, tetapi jika saya mencoba untuk kembali ke halaman 1 maka saya memiliki traceback yang sama.

Menambahkan beberapa info: pagination kursor mungkin memiliki 3 parameter: posisi, offset dan kursor. Bug dipicu ketika:

  • Anda berada di halaman yang tidak berisi objek lagi, karena objek tersebut telah dihapus (ini berfungsi dan mengembalikan daftar kosong)
  • Tautan ke halaman sebelumnya, bahkan setelah memuat ulang, menyertakan kursor dengan offset dan tanpa posisi.
  • Mengikuti tautan ini mengarah ke 500

Ini karena atribut self.page kosong, tidak ada elemen "unik" yang diasumsikan ada di halaman, halaman diasumsikan hanya berisi elemen yang memiliki posisi yang sama, dan dengan demikian algoritme memutuskan untuk menggunakan offset -pemesanan berbasis

Komentar berikut:

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

... mencakup kesalahan. Anggapan ini salah jika halaman kosong.

Memperbaiki logika dan memberi tahu algoritma untuk memeriksa apakah halaman kosong dan kemudian mengatur posisinya ke previous_position / next_position sudah cukup untuk menghilangkan bug ini.

PR akan menyusul.

Terima kasih @ewjoachim dan @tomchristie 👍

Apakah halaman ini membantu?
0 / 5 - 0 peringkat