Reputation: 35
i am trying to figure out how to solve this problem without any luck. The situation is that Author has many books divided by genres and i would like to have that when i query author it would return author and book objects divided by genres.
Author object would have these properties:
name
fantasy - would have one book based by given date
crime - would have one book based by given date
romance - would have one book based by given date
Is there a sane way to achieve this by not making thousands(if i would have that many genres) of foreign keys in author model?
class Author(models.Model):
name = models.CharField(u'Name',max_length=100)
GENRE = (
(0,u'Fantasy'),
(1,u'Crime'),
(2,u'Romance')
)
class Book(models.Model):
author = models.ForeignKey(Author)
name = models.CharField(u'Name',max_length=100)
genre = models.SmallIntegerField(u'Genre',choices=GENRE)
date = models.DateField(u'Publish date')
After closer inspection sgarza62 example seems to work bad with large amount of data. So i tried new django 1.7 feature Prefetch
authors = Author.objects.all().prefetch_related(
Prefetch("book", queryset=Book.objects.filter(genre=0,date_from__lte=datetime.datetime.now()), to_attr='x_fantasy'),
Prefetch("book", queryset=Book.objects.filter(genre=1,date_from__lte=datetime.datetime.now()), to_attr='x_crime'),
Prefetch("book", queryset=Book.objects.filter(genre=2,date_from__lte=datetime.datetime.now()), to_attr='x_romance')
)
But i have 2 issues with this, how to prefetch only one object (latest book in this example) and second, how to appy ordering based on prefetched values.
Upvotes: 1
Views: 145
Reputation: 6238
If you're querying all or several authors, I recommend prefetching related fields. This will snatch up all related objects in a single hit to the database, and store the objects in the Queryset.
authors = Author.objects.all().prefetch_related('book_set')
for author in authors:
# accessing related field will not cause a hit to the db,
# because values are cached in Queryset
for book in author.books_set:
print book.genre
If you're only querying one author, then it's not such a big deal.
author = Author.objects.get(pk=1)
her_books = author.book_set
for book in her_books:
print book.genre
Edit
I'm having a bit of trouble understanding exactly what you're going to do. But, if you're looking for the latest book of each genre, for a given author:
author = Author.objects.get(pk=1)
author_books = author.book_set.order_by('-date') # most recent, first
author_genres = set([b.genre for b in author_books])
for g in author_genres:
print next((b for b in author_books if b.genre==g), None)
Keep in mind that these operations are all on the Queryset, and are not hitting the database each time. This is good, because querying the database is an expensive operation, and most authors have a relatively small list of works, so the Querysets will generally be small.
Upvotes: 2