Django получить следующий объект, учитывая текущий объект в наборе запросов

У меня есть модель главы, и в представлении детализации главы я хочу, чтобы пользователь мог переходить к следующей и предыдущей главе, учитывая текущую главу, которую он просматривает, и каков наиболее эффективный способ сделать это в django

это моя модель главы

class Chapter(models.Model):

    title         = models.CharField(max_length=100)
    slug          = models.SlugField(blank=True,null=True,)
    module       = models.ForeignKey(Module,on_delete=models.CASCADE,null=True,blank=True,)
    content       = models.TextField()
    
    def __str__(self):
         return self.title
    

   

i have seen some answers while they use the python list method to turn the queryset in to an array but i think maybe there is a better way 

Я думал о пагинации, но написал самый быстрый способ.

class Chapter(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(blank=True,null=True,)
    module = models.ForeignKey(Module,on_delete=models.CASCADE,null=True,blank=True,)
    content = models.TextField()
    
    def __str__(self):
        return self.title

    def get_next(self):
        return Chapter.objects.filter(id__gt=self.id).order_by('id').first()
        

в HTML

Вам нужно вызвать get_next в цикле объектов главы. Я предполагаю, что URL вашей главы выглядит как detail/<id>

  <a href= {% url 'detail' obj.get_next.id }}> next chapter </a>
    

Вы можете предоставить их в контексте детального представления. Например, предыдущая_глава, текущая_глава и следующая_глава, как показано ниже

class ChapterDetailView(DetailView):
    model = Chapter
    template_name = 'chapter_detail.html'
    context_object_name = 'current_chapter'
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['previous_chapter'] = your_previous_chapter_link_if_any_else_none
        context['next_chapter'] = your_next_chapter_link_if_any_else_none
        return context

Другим подходом может быть определение урлов в models.py следующим образом

class Chapter(models.Model):
    # ... your models field
    def get_next_chapter_url(self):
        next_chapter = Chapter.objects.filter(id__gt=self.id).order_by('id').first()
        if next_chapter:
            return reverse('chapter-detail', kwargs={'id': next_chapter.id})

А в шаблоне страницы детализации главы имя объекта контекста принимается как current_chapter

<a href="{{current_chapter.get_next_chapter_url}}">Next Chapter</a>

Я бы поостерегся использовать id в качестве позиционного идентификатора, поскольку это надежно только в том случае, если главы создаются последовательно - вы не можете создать один модуль глав, затем другой, затем добавить последнюю главу в первый модуль, и вы не можете переставить главы позже. Я бы расширил модель, включив в нее поле порядка, уникальное для модуля

в вашей модели

ordering = models.PositiveSmallIntegerField()

meta:
    unique_together = (module, ordering) #so each module can only have one chapter with ordering of 1

Тогда вы можете определить метод get_next_chapter (аналогично работает метод get previous chapter)

class Chapter(models.Model):
... fields
    def get_next_chapter_url(self):
        next_chapter_index = self.ordering + 1
        next_chapter = Chapter.objects.filter(module = self.module, ordering = next_chapter_index).first()

Но... хотя это будет работать, каждое обращение к нему - это еще один вызов базы данных. Для большей эффективности можно использовать порядковый номер в представлении, чтобы получить все соответствующие главы одним запросом, например:

views.py

def view_chapter(request, module_id, chap_num):
    #define vars
    chapters = [chap_num - 1, chap_num, chap_num + 1]
    prev_chapter = None
    chapter = None
    next_chapter = None

    #perform single query
    queryset = Chapters.objects.filter(ordering__in=chapters, module_id=module_id).select_related('module') 
    #I've thrown selected_related in so you can use things like chapter.module.name in your template without additional database calls.
    
    #define context vars
    for chapter in queryset:
       if chapter.ordering == chap_num:
           chapter=chapter
       elif chapter.ordering == chap_num - 1:
           prev_chapter = chapter
       elif chapter.ordering == chap_num + 1:
           next_chapter = chapter

    context = {"chapter": chapter, "prev-chapter": prev_chapter, "next_chapter": next_chapter}
        

и затем в вашем шаблоне

<a href="{% url 'view chapter' chapter.module_id prev_chapter.ordering %}"> previous chapter</a>
Вернуться на верх