Skip to content

Utility class for Django that helps determine the page number on which a specific object appears in a paginated list, either from a QuerySet or via a ForeignKey relationship.

License

Notifications You must be signed in to change notification settings

StanislavJPG/django-page-resolver

Repository files navigation

django-page-resolver

PyPI Downloads

This is python utility for Django that helps determine the page number on which a specific model instance appears within a paginated queryset or related object set. It also includes a Django templatetag for rendering HTMX + Bootstrap-compatible pagination with support for large page ranges and dynamic page loading.

Imagine you're working on a Django project where you want to highlight or scroll to a specific item on a paginated list — for example, highlighting a comment on a forum post. To do this, you need to calculate which page that comment appears on and then include that page number in the URL, like so:

localhost:8000/forum/posts/151/?comment=17&page=4

This allows you to directly link to the page where the target item exists. Instead of manually figuring this out, use FlexPageResolver or PageResolverModel.

See Usage.

Installation

pip install django-page-resolver

Then you have to pass django_page_resolver to your INSTALLED_APPS:

INSTALLED_APPS = [
  ...
  'django_page_resolver',
  ...
]

Usage

Using of page-resolver to determine object's page location in paginated queryset.
There is a two ways to do so:

  1. Using abstract model PageResolverModel:

    from django_page_resolver.models import PageResolverModel
    
    class Comment(PageResolverModel):
        ...
    
    # OR
    
    class Post(PageResolverModel):
        ...

    And then you have next API:

    comment = Comment.objects.get(pk=27)
    comment_page_number = comment.get_page_from_queryset(order_by='-relevancy_value', items_per_page=15)
    # comment_page_number -> return 3
    
    # OR
    
    post = Post.objects.get(pk=120)
    comment_page_number_from_post = post.get_page_from_nested_object(target_child_instance=comment, order_by='-relevancy_value', items_per_page=15)
    # comment_page_number_from_post -> return 3
  2. Using page_resolver class instance to do the same as was described above.

    from django_page_resolver.resolvers import page_resolver
    
    comment = Comment.objects.get(pk=27)
    comment_page_number = page_resolver.get_page_from_queryset(
     target_instance=comment, 
     order_by='-relevancy_value', 
     items_per_page=15
    )
    # comment_page_number -> return 3
    
    # OR
    
    post = Post.objects.get(pk=120)
    comment_page_number_from_post = page_resolver.get_page_from_nested_object(
      parent_instance=post,
      target_child_instance=comment,
      order_by='-relevancy_value',
      items_per_page=15
    )
    # comment_page_number_from_post -> return 3

Parameters:

get_page_from_nested_object:

  • parent_instance: The parent model instance (e.g., Post). Required and uses only from page_resolver instance.
  • target_child_instance: The related model instance to locate (e.g., Comment). Required.
  • siblings_qs: Optional queryset to search in. If not provided, will use target_child_instance's model.
  • related_name: The related name on the parent that accesses the child objects (e.g., 'comments'). (By default takes verbose_name_plural from the Model's meta.)
  • order_by: Field used to order the queryset. Default is None.
  • items_per_page: The pagination size (number of items per page). Required.

get_page_from_queryset:

  • target_instance: The instance whose page number we want to find. Required and uses only from page_resolver instance.
  • queryset: Optional queryset to search in. If not provided, will use target_instance's model. (By default takes default __class__.objects.all() queryset from the Model)
  • order_by: Field to order the queryset by. Default is None.
  • items_per_page: Number of items per page for pagination. Required.

And then you have it!

Front-end utilities

Prerequisites:

  1. HTMX js-library.
  2. Bootstrap 5.0+

If you've set up page_resolver logic, you can use additional template tags and JavaScript utilities to automatically scroll to a specific instance on the page.

The JavaScript reads URL parameters that contain the unique identifier of the instance to scroll to.

Example:

  1. Load page_resolvers templatetags in your HTML:
    {% load page_resolvers %}

  2. Include the script and CSS styles in your HTML:

    <script src="{% static 'resolvers/js/page_resolvers.js' %}"></script> 
    <link type="text/css" href="{% static 'resolvers/css/page_resolvers.css' %}" rel="stylesheet">
    
  3. Inside your {% for %} loop, register the object to be scrolled to:
    {% register_scroll_obj_unique_pk instance_pk=comment.pk %} (You can use another unique identifier, such as a UUID.)

    This will mark the object as a scroll target.

  4. Finally, add this tag inside the class attribute of your main HTML element:
    {% classes_by_lookup_url instance_pk=comment.pk url_lookup='comment' %}(Or other unique identified like UUID)

    Like so: <div class="container {% classes_by_lookup_url instance_pk=comment.pk url_lookup='comment' %}">...</div>

    classes_by_lookup_url takes the following arguments:

    • instance_pk: The unique ID of the instance (could be UUID or another PK), that the page should scroll to.
    • url_lookup: The name of the parameter in the URL. (Like /?comment=12&page=2, where url_lookup is a comment parameter name.)

(See examples in /examples/templatetags/register_instance_scroll.html within the library directory.)

And then you will have it:
Scroller example


Additionaly, you can have handsome dynamic HTMX+Bootstrap HTML paginator via templatetag!

Load page_resolvers templatetags into your HTML:
{% load page_resolvers %}

Then just pass render_htmx_pagination templatetag with htmx_target argument in your HTML code like so:

{% render_htmx_pagination "#comment-list-body-js" %}

Note: You need to add render_htmx_pagination templatetag inside tag that has htmx_target selector which is you have specified. This is required because of rerender the pagination block with objects list while htmx-request. See examples in examples folder.

That will render default bootstrap pagination with HTMX and nice-UI large pages count support and i18n. You can also add some classes to every element in pagination:

{% render_bootstrap_pagination '#post-list-js' ul_class="some-outstanding-class" li_class="more-class" a_class="text-danger" %}

And this is what you get then:

Pagination example

Contributing

You’re welcome to contribute to django-page-resolver by submitting pull requests, suggesting ideas, or helping improve the project in any way. Let’s make this library better together!

About

Utility class for Django that helps determine the page number on which a specific object appears in a paginated list, either from a QuerySet or via a ForeignKey relationship.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published