Reputation: 3832
I have the following structure in a django application
class Course(models.Model):
name = models.CharField(_("Course Name"), max_length=50)
class Module(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
name = models.CharField(_("Module Name"), max_length=50)
number = models.IntegerField(_("Module Number"))
class Chapter(models.Model):
module = models.ForeignKey(Module, on_delete=models.CASCADE)
name = models.CharField(_("Chapter Name"), max_length=50)
number = models.IntegerField(_("Chapter Number"))
I wanted to fetch all Chapters that is contained in a course, with the order of the module (based on module number) and the order of chapter ( based on chapter number )
for example the hierarchy can be like this
Course 1 has
Module 1
-- Chapter number=1 (1.1)
-- Chapter number=2 (1.2)
-- Chapter number=3 (1.3)
Module 2
-- Chapter number=1 (2.1)
-- Chapter number=2 (2.2)
-- Chapter number=3 (2.3)
Course 2 has,
Module 1
-- Chapter 1
-- Chapter 2
-- Chapter 3
Module 2
-- Chapter 1
-- Chapter 2
-- Chapter 3
I would like to fetch All chapters in Course 1 such in the order
[ (1.1), (1.2), (1.3), (2.1), (2.2), (2.3) ] and so on...
Then I would like to implement next_chapter property in model which would give Chapter 2.1 object after 1.3.
class Chapter(models.Model):
@property
def next_chapter(self):
???
When i fetch all chapters with the query
Chapter.objects.filter(module__in=Course1.module_set.all())
I'm getting the chapters ordered by
[ (1.1), (2.1), (1.2), (2.2), (3.1), (3.2) ]...
As the chapter number is 1 2 3 etc...
Upvotes: 0
Views: 98
Reputation: 169388
order_by
clauses can also use __
to reach across relationships.
Similarly, you can simplify the course lookup to span the relationship to avoid the __in
stuff:
qs = Chapter.objects.filter(module__course=Course1).order_by(
"module__number", "number"
)
If you have a chapter and you want to get the next chapter, you'll want something like this – the idea is, that since the qs
queryset from above is in correct order already, all we need to do is find
and then take the first one (in that correct sorted order):
current_chapter = ... # assume this is the current chapter
next_qs = qs.filter( # qs is from above
Q(module=current_chapter.module, number__gt=current_chapter.number) | # same module, next chapter number
Q(module__number__gt=current_chapter.module.number) # next module
)
next_chapter = next_qs.first() # first filtered result in same sort order
Upvotes: 1