Reputation: 265
I have the following models:
class PodcastPlatform(models.Model):
name = models.CharField(max_length = 60)
badge_path = models.FilePathField(path = settings.BADGES_DIR, unique = True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Podcast(models.Model):
name = models.CharField(max_length = 60)
description = models.CharField(max_length = 400)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
class Meta:
ordering = ['-created', '-updated']
def __str__(self):
return self.name
class PodcastLink(models.Model):
podcast_platform = models.ForeignKey(PodcastPlatform, on_delete = models.CASCADE)
podcast = models.ForeignKey(Podcast, on_delete = models.CASCADE)
url = models.URLField(max_length = 400, verbose_name = 'URL')
class Meta:
ordering = ['podcast_platform__name']
unique_together = ['podcast_platform', 'podcast']
def __str__(self):
return self.podcast_platform.name
def get_badge_url(self):
return static(re.search('img/.{0,}', self.podcast_platform.badge_path).group())
I want to filter the instances of the Podcast
model where they have at least one link or a related instance of the PodcastLink
model.
To do it, I did the following:
Podcast.objects.filter(podcastlink__gte = 1)
Returning the following QuerySet:
<QuerySet [<Podcast: Libero tenetur>, <Podcast: Libero tenetur>, <Podcast: Libero tenetur>, <Podcast: Assumenda iusto>, <Podcast: Assumenda iusto>, <Podcast: Assumenda iusto>, <Podcast: Assumenda iusto>, <Podcast: Explicabo>, <Podcast: Explicabo>, <Podcast: Explicabo>, <Podcast: Explicabo>, <Podcast: Explicabo>]>
I get the expected result, but instead I notice that the instances are repeating... Try the following and in the same way I got the same result (repeated instances):
Podcast.objects.filter(Q(podcastlink__gte = 1))
Why do I get repeated instances? as i understand this should not happen... plus it's weird...
Upvotes: 0
Views: 683
Reputation: 34942
I assume that you have related_name='podcastlink'
on PodcastLink.podcast
or some logic that sets it automatically, otherwise your code should not work.
Podcast.objects.filter(podcastlink__gte=1)
. Does not make any sens. podcastlink
is a relation not a number. I guess it works by chance with podcastlink__gte=1
but wouldn't work with podcastlink__gte=2
. To test existence of a relation you should rather use podcastlink__isnull=False
:
Podcast.objects.filter(podcastlink__isnull=False)
However, this query will join Podcast
and PodcastLink
and return one entry for each combination. As you can have many PodcastLink
per Podcast
, you may get duplicates of Podcast
. To avoid these duplicates, use `distinct().
Podcast.objects.filter(podcastlink__isnull=False).distinct()
Another approach is to actually count the number of links per podcast:
from django.db.models import Count
Podcast.objects.annotate(nb_links=Count('podcastlink')).filter(nb_links__gte=1)
In this case, you don't need to use distinct()
because you aggregated the PodcastLink
as a counter in Podcast
. Using this method, you can also now decide to filter Podcast
having at least 2 links as well if you want:
Podcast.objects.annotate(nb_links=Count('podcastlink')).filter(nb_links__gte=2)
Upvotes: 1
Reputation: 3527
You can use distinct()
to get unique instances. You probably also want to filter on the count:
Podcast.objects.filter(podcastlink__count__gte = 1).distinct()
Upvotes: 1