I could need some help on designing my models and their relationships.
Each Tag can be used in multiple instances of Book (e.g. "Lord of the Rings" and "Discworld" both have the tag "Fantasy").
Each Aspect can be used in multiple instances of Tag (e.g. "Fantasy" and "Scifi" both have the aspect "World Detail").
Here is a descriptive image:
(wow, these are big ones)
Because i want to store user ratings for each aspect that is related to a specific book.
That's the main problem here. I want to model this in Django and this is what i got so far:
class Book(models.Model):
title = models.CharField(max_length=100, unique=True)
tags = ManyToManyField(Tag)
# the following line is a workaround...
aspects = models.ManyToManyField(Aspect, through='BookAspect')
class Tag(models.Model):
name = models.CharField(max_length=100)
aspects = models.ManyToManyField(Aspect)
class Aspect(models.Model):
name = models.CharField(max_length=100)
# this class is a workaround ...
class BookAspect(models.Model):
book = models.ForeignKey(Book)
aspect = models.ForeignKey(Aspect)
# this is from django-ratings
rating = RatingField(range=5, can_change_vote=True, allow_delete=True, blank=True)
class Meta:
unique_together = ('book', 'aspect',)
In addition to the models, i created a m2m_changed
signal listener for action="post_add"
:
@receiver(m2m_changed, sender=Book.tags.through)
def m2m_changed_book(sender, instance, action, reverse, pk_set, **kwargs):
if action is not 'post_add' or reverse:
return
# iterate through all newly created tags to manually add
# the aspects from each tag to the BookAspect table
for tag_id in pk_set:
aspects = Tag.objects.get(pk=tag_id).aspects.all()
# this is annoying, i have to manually set the relations...
for aspect in aspects:
bookAspect = BookAspect(book=instance, aspect=aspect)
bookAspect.save()
Although that should work, i would need additional logic to deal with removed tags.
But the real annoying thing is that i have to manually add the per-book-aspect-relations so that i can store user ratings. Naturally i need different ratings for the same aspect of different books.
I would change:
class Book(models.Model):
title = models.CharField(max_length=100, unique=True)
tags = ManyToManyField(Tag)
Since the aspects
field has nothing to do in the Book
class.
I have no idea of why you wrote BookAspect
. About user ratings, you can do:
class BookRating(models.Model):
book = models.ForeignKey(Book)
aspect = models.ForeignKey(Aspect)
rating = models.RatingField()
# Method to rate a book and check the aspect belong to one of the book's tags
def rate(book, aspect):
# check the aspect is related to a tag which is also related to the book
# if so, save a new rating entry
# You can also override the save() method to check the aspect is valid for the book