armin NaCl
armin NaCl
خواندن ۳ دقیقه·۳ سال پیش

pagination DRF limit and page



بدون شک میشه گفت که django rest framework یکی از قدرتمند ترین ابزار های جنگو به طوری که همچیز رو خودش به صورت خیلی حرفه ای هندل کرده و برای کاستمایز کردنش فقط کافیه یه اوررایت ساده انجام بدی .

چند وقت پیش می خواستم به دیتا های سریالایزشدم پجینیشون یا صحفه بندی بدم جوری که کلاینت سایت خودش بتونه مقدار محتوای هر صحفه رو تعین بکنه . خوشبختانه drf خودش این قابلیت رو برای استفاده گذاشته بود فقط کافی بود توی فایل settings پروژه و توی سکشن REST_FRAMEWORK مقدار rest_framework.pagination.LimitOffsetPagination قرار می دادی

REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination' }

توی این نوع پجینیشن کاربر با وارد کردن limit که تعداد محتوای هر پیجه و offset که میشه اندیکس اولین داده تو اون صحفه تقسیم بندی خودش رو انجام میده .

ولی مشکلی که ما با این نوع پجینیشن داشتیم این بود که فرانت نمی تونست مقدار offset رو جنرییت کنه و از اون طرف هم اگه میکرد یکمی برای کاربر گیج کننده بود . واسه همین تصمیم گرفتیم که به جای offset از page استفاده کنیم . راه اولی که پیدا کردم ساختن چندتا کلاس پجینیشن با limit های متفاوت بود که بعد توی view با خوندن query_params پجینیشن مورد نظر رو انتخاب بکنه . این روش رو می تونید تو خود داکیومنت drf بخونید . ولی این روش مد نظر من نبود چون بازم مقدار limit محدود به من بود و اگه می خواستم این محدودیت رو کمتر کنم بایستی کلی کلاس می نوشتم و این اصلا جالب نبود.

واسه همین خودم دست به کار شدم و شروع کردم به اوررایت کردن کلاس های drf

من اومدم و تو یکی از app هم یه دایرکتوری pagination ساختم و توش یه فایل به اسم LimitPagePagination که توش یه کلاس به همون اسم داره و از LimitOffsetPagination ارث بری می کنه اولین کاری که کردم اسم query_param رو از offset به page تغیر دادم

class LimitPagePagination(LimitOffsetPagination): offset_query_param = 'page'

بعد باید با استفاده از فانکشن های خود LimitOffsetPagination مقدار offset رو از توی page جنریت می کردم که به صورت زیر انجام دادم

def get_offset(self, request): try: page_num = _positive_int( request.query_params[self.offset_query_param],) limit = _positive_int( request.query_params[self.limit_query_param],) return (page_num - 1)*limit except (KeyError, ValueError): return 0

کار ما تقریبا تمومه ولی هنوز یه چیزی مونده اگه با drf کار کرده باشید میدونید که وقتی پجینیت می کنید لینک صحفه قبل و بعد رو براتون اماده می کنه ولی با این تغیرات که ما دادیم این لینک همیشه درست نیستن واسه اینکه این مشکل رو هم حل کنیم متد های get_next_link و get_previous_link رو هم اوررایت کنیم

def get_next_link(self): if self.offset + self.limit >= self.count: return None url = self.request.build_absolute_uri() url = replace_query_param(url, self.limit_query_param, self.limit) offset = self.offset//self.limit + 2 return replace_query_param(url, self.offset_query_param, offset) def get_previous_link(self): if self.offset <= 0: return None url = self.request.build_absolute_uri() url = replace_query_param(url, self.limit_query_param, self.limit)
if self.offset//self.limit -1 <= 0: return remove_query_param(url, self.offset_query_param) offset = self.offset//self.limit return replace_query_param(url, self.offset_query_param, offset)

حالا فقط کافیه که تو settings پروژه ادرس این کلاس مون رو به عنوان پجینیشن دیفالت قرار بدیم

REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': '<your_app>.<your_file>.LimitPagePagination' }

خب امیدوارم که بددردبخور بوده باشم در اخر اینو بگم این دقیقا مثل یه کلک زدن به یوزر میمونه و همنطور که می بینید منطق برنامه کاملا داره با offset کار می کنه ولی موقعه نمایش دوباره به page تبدیل میشه.

خوشحال میشم اگه روش بهتری دارید حتما تو کامنت ها با من درمیون بزارید .


drfdjangopaginationrestcode
شاید از این پست‌ها خوشتان بیاید