0

I have a database model like this:

class RssNewsItem(models.Model):
    title = models.CharField(max_length=512)
    description = models.TextField()
    image_url = models.CharField(max_length=512)
    published = models.DateTimeField(auto_now=True)
    url = models.CharField(max_length=512, default='')
    author = models.CharField(max_length=128, default='')

I would like to 'promote' a certain author by selecting 3 of its news-items and 7 items from other authors (making a list of 10 news-items) and order them by -published. The position of the promoted news-items on the list is irrelevant. Numbers are also not important. It just have to be that promoted news-items cover 30% of the list.

Let's suppose that I want to promote 'author1' and I have 6 total authors in my website.

Is this possible with Django? (I would like to avoid iterating through lists or querysets)

xpanta
  • 8,124
  • 15
  • 60
  • 104

3 Answers3

3
from itertools import chain
q1 = RssNewItem.objects.filter(author="author1").order_by("-published")[:3]
q2 = RssNewItem.objects.exclude(author="author1").order_by("-published")[:7]
q = list(chain(q1, q2))

P.s. Here's a good SO answer on merging querysets:

How to combine 2 or more querysets in a Django view?

  • itertools is fast but obviously the result is a list and can't be further queried
  • you can convert to lists and append/extend: list(q1).extend(list(q2)). Same problem as above and slower.
  • as they are the same model, you can do: q = q1 | q2 to keep them as a QuerySet.
Community
  • 1
  • 1
Timmy O'Mahony
  • 53,000
  • 18
  • 155
  • 177
  • or you can just do `q1.append(list(q2))` Since they are of the same type. That way you can do away with an import – karthikr Jun 21 '13 at 10:14
  • Good spot. I'll leave it as it is as I'm in the habit of using `itertools` – Timmy O'Mahony Jun 21 '13 at 10:15
  • what about `>>> li = ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li.extend(["two", "elements"]) >>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']` – suhailvs Jun 21 '13 at 10:25
  • @karthikr Just to follow up, you need to first convert `q1` to a list: `list(q1).extend(list(q2))` – Timmy O'Mahony Jun 21 '13 at 10:38
  • Thanks, when I do `q = q1 | q2` I get this: `Cannot combine queries once a slice has been taken` – xpanta Jun 21 '13 at 11:41
  • ok use `chain(q1, q2)`. i don't know what `q1|q2` will do. But it worked when i checked – suhailvs Jun 21 '13 at 12:08
1
class RssNewsItemManager(models.Manager):
    def get_rsslist_with_promoted(self,auth):
        prom=self.objects.filter(author=auth).order_by("-published")[:3]
        unprom=self.objects.exclude(author=auth).order_by("-published")[:7]
        return prom|unprom

class RssNewsItem(models.Model):
    title = models.CharField(max_length=512)
    description = models.TextField()
    image_url = models.CharField(max_length=512)
    published = models.DateTimeField(auto_now=True)
    url = models.CharField(max_length=512, default='')
    author = models.CharField(max_length=128, default='')
    objects = RssNewsItemManager()
suhailvs
  • 20,182
  • 14
  • 100
  • 98
  • This is interesting, however I would like to ask if this is possible because adding querysets is not allowed, unless `prom` and `unprom` are not querysets. – xpanta Jun 21 '13 at 10:20
0

Solution for 10 items, or any small number:

qs = RssNewsItem.objects.order_by('-published')
promoted = list(qs.filter(author=promoted_author)[:count_of_promoted])
to_fill = list(qs.exclude(author=promoted_author)[:total_count-len(promoted)])
to_return = sorted(promoted + to_fill, key=lambda rni: -rni.published)
return to_return
dswistowski
  • 171
  • 4