How to swap between detail views for user specific datasets in django (python)?

On a Table (lets call it Items) I open the detail views for the item. On the detail view I want a "next" and a "previous" button. The button should open the next items detail view. I cannot just traverse through all datasets because the user cannot access other users datasets.

I though about using a doubly linked list where the data in the nodes contain the id of the current dataset and as pointers the next and previous item id. When the user reaches the tail he automatically goes to the head and the other way around. But I dont want to load this list every time the user is opening the next detail view.

Is there a ressource friendly way to swap between detail views without just increasing the id?

You can limit the query to a single object using the list slicing notation, but to achieve this, you will need to customize the get_object method.

# For next item
class NextItemDetailView(LoginRequiredMixin, DetailView):
    # using the same template as ItemDetailView
    template_name = "item.html"
    def get_object(self, queryset=None):
        pk = self.kwargs.get(self.pk_url_kwarg)
        try:
            obj = self.request.item_set\
                .filter(id__gt=pk)\
                .order_by('id')[:1].get()
        except Item.DoesNotExists:
            raise Http404('No more item')
        return obj

Then In urls.py

urlpatterns = [
    # ...
    path("item/<int:pk>/next/", views.NextItemDetailView.as_view(), name="next-item")
]

And in template

<a class="button" href="{% url 'next-item' item.id %}">next</a>

Getting previous item is similar. Just change the filter condition to lt instead of gt and the ordering to '-id' for descending order.

I think you're overcomplicating this! You don't need a linked list or complex data structures. Django's ORM can handle this efficiently with simple database queries.

You can do something like this

class ItemDetailView(LoginRequiredMixin, DetailView):
    model = Item
    template_name = "item.html"
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        current_item = self.get_object()
        
        # Get next item (first item with ID > current, ordered by ID)
        next_item = Item.objects.filter(
            user=self.request.user,  # Security: only user's items
            id__gt=current_item.id
        ).order_by('id').first()
        
        # Get previous item (first item with ID < current, ordered descending)
        prev_item = Item.objects.filter(
            user=self.request.user,
            id__lt=current_item.id
        ).order_by('-id').first()
        
        # Handle circular navigation (wrap around)
        if not next_item:
            next_item = Item.objects.filter(user=self.request.user).order_by('id').first()
        if not prev_item:
            prev_item = Item.objects.filter(user=self.request.user).order_by('-id').first()
        
        context['next_item_id'] = next_item.id if next_item else None
        context['prev_item_id'] = prev_item.id if prev_item else None
        
        return context

And in your template:

{% if prev_item_id %}
    <a class='button' href='{% url "item" prev_item_id %}'>Previous</a>
{% endif %}

{% if next_item_id %}
    <a class='button' href='{% url "item" next_item_id %}'>Next</a>
{% endif %}
Вернуться на верх