Django-rest-framework: ์ฟผ๋ฆฌ ์„ธํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ CursorPagination์— ๋Œ€ํ•œ IndexError

์— ๋งŒ๋“  2019๋…„ 03์›” 11์ผ  ยท  4์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: encode/django-rest-framework

์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • [X] Django REST ํ”„๋ ˆ์ž„์›Œํฌ์˜ master ๋ถ„๊ธฐ์— ํ•ด๋‹น ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
  • [x] ๊ณต๊ฐœ ํ‹ฐ์ผ“๊ณผ ๋น„๊ณต๊ฐœ ํ‹ฐ์ผ“ ๋ชจ๋‘์—์„œ ์œ ์‚ฌํ•œ ๋ฌธ์ œ๋ฅผ ๊ฒ€์ƒ‰ํ–ˆ์ง€๋งŒ ์ค‘๋ณต์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • [x] ์ด๊ฒƒ์€ ์‚ฌ์šฉ ์งˆ๋ฌธ์ด ์•„๋‹™๋‹ˆ๋‹ค. (๋Œ€์‹  ํ† ๋ก  ๊ทธ๋ฃน์œผ๋กœ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.)
  • [x] ์ด๊ฒƒ์€ ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. (๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ์ œ3์ž ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜•ํƒœ์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.)
  • [x] ๋ฌธ์ œ๋ฅผ ๊ฐ€๋Šฅํ•œ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๋กœ ์ค„์˜€์Šต๋‹ˆ๋‹ค.
  • [x] ํ’€ ๋ฆฌํ€˜์ŠคํŠธ์— ์‹คํŒจํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ํฌํ•จ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. (๊ทธ๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์—๋„ ๋ฌธ์ œ๋ฅผ ์ˆ˜๋ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

์žฌํ˜„ ๋‹จ๊ณ„

  1. CursorPagination ํ™œ์„ฑํ™”
  2. ํŽ˜์ด์ง€ ๋งค๊น€์ด์žˆ์„๋งŒํผ ํ•ญ๋ชฉ์„ ๋งŒ๋“œ์‹ญ์‹œ์˜ค.
  3. 2ํŽ˜์ด์ง€๋กœ ์ด๋™
  4. ์…ธ ๋‚ด์˜ ๋ชจ๋“  ํ•ญ๋ชฉ ์‚ญ์ œ
  5. ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ
  6. ์˜ค๋ฅ˜ ๋ณด๊ธฐ

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™

์†”์งํžˆ ๋งํ•ด์„œ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ๋นˆ ํŽ˜์ด์ง€์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‹ค์ œ ํ–‰๋™

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

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@ewjoachim ๊ณผ @tomchristie ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘

๋ชจ๋“  4 ๋Œ“๊ธ€

ํ•ด๋ณผ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. 4์›” 15์ผ๊นŒ์ง€ ๋‚˜์—๊ฒŒ์„œ ์†Œ์‹์ด ์—†์œผ๋ฉด ๋‚ด๊ฐ€ ๋” ์ด์ƒ ๋‰ด์Šค์— ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ•˜์‹ญ์‹œ์˜ค.

๊ธฐ๋ก์„ ์œ„ํ•ด ํ‹ฐ์ผ“์˜ ์ •ํ™•ํ•œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅผ ๋•Œ ๋ฐ˜๋ฐ•ํ•  ์ˆ˜ ์—†์ง€๋งŒ 1ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€๋ ค๊ณ  ํ•˜๋ฉด ๋™์ผํ•œ ์—ญ์ถ”์ ์„ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ •๋ณด ์ถ”๊ฐ€: ์ปค์„œ ํŽ˜์ด์ง€ ๋งค๊น€์—๋Š” ์œ„์น˜, ์˜คํ”„์…‹ ๋ฐ ์ปค์„œ์˜ 3๊ฐ€์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ ๋ฒ„๊ทธ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

  • ๊ฐœ์ฒด๊ฐ€ ์‚ญ์ œ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ด์ƒ ๊ฐœ์ฒด๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€์— ์žˆ์Šต๋‹ˆ๋‹ค(์ด๋Š” ์ž‘๋™ํ•˜๊ณ  ๋นˆ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•จ).
  • ๋‹ค์‹œ ๋กœ๋“œํ•œ ํ›„์—๋„ ์ด์ „ ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ๋งํฌ์—๋Š” ์˜คํ”„์…‹์ด ์žˆ๊ณ  ์œ„์น˜๊ฐ€ ์—†๋Š” ์ปค์„œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด 500

์ด๋Š” self.page ์†์„ฑ์ด ๋น„์–ด ์žˆ๊ณ  ํŽ˜์ด์ง€์— "๊ณ ์œ ํ•œ" ์š”์†Œ๊ฐ€ ์—†๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ํŽ˜์ด์ง€๊ฐ€ ๋™์ผํ•œ ์œ„์น˜๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๋งŒ ํฌํ•จํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•˜๋ฏ€๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์˜คํ”„์…‹์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด๊ฒฐํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. - ๊ธฐ๋ฐ˜ ์ฃผ๋ฌธ.

๋‹ค์Œ ์˜๊ฒฌ:

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

... ์˜ค๋ฅ˜๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ์ด ๊ฐ€์ •์€ ์ž˜๋ชป๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋กœ์ง์„ ์ˆ˜์ •ํ•˜๊ณ  ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ํŽ˜์ด์ง€๊ฐ€ ๋น„์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ ๋‹ค์Œ ์œ„์น˜๋ฅผ previous_position / next_position ๋กœ ์„ค์ •ํ•˜๋ฉด ์ด ๋ฒ„๊ทธ๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

ํ™๋ณด๊ฐ€ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

@ewjoachim ๊ณผ @tomchristie ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰