Multiple many-to-many database modeling in Django

I may need help developing my models and their relationships.

Short review

  • Book : The names of the books (for example, Lord of the Rings).
  • Tag : tags associated with books (e.g. Fantasy, Wizard, Epic Battle)
  • Aspect : aspects related to tags, or, in other words, things that users can evaluate when a specific tag occurs, for example:
    • for "Fantasy" tags can be "World Detail" and "Time"
    • for the "Epic fight" tag, aspects can be "Gore Level" and "Fight tension".

Each tag can be used in several copies of the Book (for example, “Lord of the Rings” and “Discwich”, both have a tag “Fantasy”).

Each aspect can be used in several instances of Tag (for example, "Fantasy" and "Scifi", both have the "World Detail" aspect).

Here is a descriptive image:

enter image description here

(wow that's big)

"Why do you need an extra BookAspect table?" you can ask?

Because I want to store user ratings for every aspect related to a particular book.

This is the main problem here. I want to simulate 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 m2m_changed 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 this should work, I will need additional logic to eliminate the deleted tags.

But the real annoyance is that I have to manually add relationships between books and aspects so that I can store user ratings. Naturally, I need different grades for the same aspect of different books.

Questions

1. Is this the right way to do this, am I missing something or is it not as difficult as I think?

2. Is it possible to “automate” the Book-Aspect relationship, so I don’t need to manually update the relationship?

3. How can I simulate it in Django?

+6
source share
1 answer
  • I think this can be done with greater simplicity.
  • I think to some extent.
  • 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 with the Book class.

I have no idea why you wrote BookAspect . About user ratings you can:

 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 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 
+2
source

Source: https://habr.com/ru/post/915710/


All Articles