I am learning the coding style through reading the Django source code:
In the pagination.py Pagination, the source code define a private method to instantiate the class Page.
def page(self, number):
"""
Returns a Page object for the given 1-based page number.
"""
number = self.validate_number(number)
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return self._get_page(self.object_list[bottom:top], number, self)
def _get_page(self, *args, **kwargs):
"""
Returns an instance of a single page.
This hook can be used by subclasses to use an alternative to the
standard :cls:`Page` object.
"""
return Page(*args, **kwargs)
I think it more readable if instantiate it with method page as
def page(self, number):
"""
Returns a Page object for the given 1-based page number.
"""
number = self.validate_number(number)
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return Page(self.object_list[bottom:top], number, self)
What's the advantage to define separate _get_page
method in extra step?
The advantage is, like the docstring says (that you also show in your question):
This hook can be used by subclasses to use an alternative to the standard :cls:
Page
object.
If we just want to change the Page
class, we override _get_page()
but don't need to repeat the whole logic that builds top
and bottom
parameters in the page()
method (DRY principle).
We would override the page()
method only if we want to change some part of the logic calculated in there.
It is already in the comment of the function:
def _get_page(self, *args, **kwargs):
"""
Returns an instance of a single page.
This hook can be used by subclasses to use an alternative to the
standard :cls:`Page` object.
"""
return Page(*args, **kwargs)
Imagine that later you write your own paging method (with a more advanced Page
object), then you will need not only to fix all your code by using the new Page
object (and perhaps some still need to use the "old" page object), but you would have to patch some parts of the Django codebase as well.
By introducing a method that acts as a wrapper around the Page
constructor, we can monkey patch the construction of a page.
For example we can define our own speciale Page
class:
class SpecialPage(Page):
# ...
pass
and then we can add a monkey_patching.py
file to any of the applications, and write:
import django.core.paginator
def _new_get_page(self, *args, **kwargs):
return SpecialPage(*args, **kwargs)
django.core.paginator._get_page = _new_get_page
Typically by creating "levels of indirection", one allows a user to fix certain parts at those indirection levels, and thus can inject their own implementation into Django parts.
Subclassing is the most straightforward fix, but not per se the only one. Perhaps you want to preprocess certain arguments, or post-process the Page
object, or fire some "signals" to all kind of functions that do something with the Page
that is created. Perhaps for a page those are not very logical things, but for other object constructions (like model instances), adding those levels of indirection are more sensical.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With