Olivier Pons
Olivier Pons

Reputation: 15816

Is there a shorter / simpler query than this one?

I have this very simple models: a Clip can have many AdBase and an AdBase can have many Clip's.

class AdBase(models.Model):
    title = models.CharField(max_length=200, default=None, null=True,
                             blank=False)

class Clip(models.Model):
    ads = models.ManyToManyField(AdBase, related_name='clips',
                                 through='ClipAd')


class ClipAd(models.Model):
    clip = models.ForeignKey(Clip, on_delete=models.CASCADE,
                             blank=False, null=False)
    ad = models.ForeignKey(AdBase, on_delete=models.CASCADE,
                           blank=False, null=False)
    position = models.IntegerField(default=0, blank=False, null=False)

I want in the class Clip, a query which returns all its clips ordered by their position.

I've come up with this:

class Clip(models.Model):
    ads = models.ManyToManyField(AdBase, related_name='clips',
                                 through='ClipAd')
    def ads_by_position(self):
        # there might be a simpler query than this:
        return [a.ad
                for a in ClipAd.objects.filter(clip=self).order_by('position')]

But I'm sure there's a simpler syntax using the ads property of my class Clip but I didn't find one.

Any idea (or is this the only solution)?

Upvotes: 0

Views: 51

Answers (2)

ilhnctn
ilhnctn

Reputation: 2210

You can use prefetch_related for this purpose. It will also decrease your DB load a lot. Also, using custom managers worths a look.

class ClipManager(models.Manager):
    def get_with_adds():
        return Clip.objects.filter().prefetch_related("ads")


class AdBase(models.Model):
    title = models.CharField(max_length=200, default="")

class Clip(models.Model):
    ads = models.ManyToManyField(AdBase, related_name='clips',
                                 through='ClipAd')
    objects = ClipManager()


class ClipAd(models.Model):
    clip = models.ForeignKey(Clip, on_delete=models.CASCADE)
    ad = models.ForeignKey(AdBase, on_delete=models.CASCADE)
    position = models.IntegerField(default=0)

clips_with_adds = Clip.objects.get_with_adds()

Upvotes: 2

Igor Moraru
Igor Moraru

Reputation: 7739

You can access related many to many items directly:

def ads_by_position(self):
    return self.ads.all().order_by('position')

Upvotes: 0

Related Questions