Django many to many relation, include all IDs in queryset in both directions

1.8k views Asked by At

I have 2 models connected via M2M relation

class Paper(models.Model):
  title = models.CharField(max_length=70)
  authors = models.ManyToManyField(B, related_name='papers')

class Author():
  name = models.CharField(max_length=70)
  • Is there a way to include authors as all related authors' IDs (and maybe name somehow)?

  • Is there a way to include papers IDs as reverse relation (and maybe title as well)?

Author.objects.all().annotate(related_papers=F('papers'))

this only adds id of one paper, first one it finds I think.

Furthermore, changing related_papers to papers gives an error:

ValueError: The annotation ‘papers’ conflicts with a field on the
model.
2

There are 2 answers

2
Guillaume On BEST ANSWER

From what I understand in your comments, you're using DRF. I will give you 2 answers.

1) If you're talking about model serializer, you can use PrimaryKeyRelatedField :

class AuthorSerializer(serializers.ModelSerializer):
    papers=serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Author
        fields = ['name', 'papers']

class PaperSerializer(serializers.ModelSerializer):
    class Meta:
        model = Paper
        fields = '__all__'

This will return the IDs for the other side of the relationship whether you're on Paper or Author side. That will return the primary keys, not a representation of the object itself.

2) Now you're also talking about performance (e.g. database hit at each iteration).

Django (not DRF-specific) has a queryset method to handle preloading related objects. It's called prefetch_related.

For example, if you know you're going to need the relation object attributes and want to avoid re-querying the database, do as follow:

Author.objects.all().prefetch_related('papers') 
# papers will be already loaded, thus won't need another database hit if you iterate over them.
0
Dmytro On

Actually, it has already been implemented for you. You should include a Many-to-Many relationship to author in your Paper model like this:

class Paper(models.Model):
  title = models.CharField(max_length=70)
  authors = models.ManyToManyField(Author, related_name='papers')

That gives you the opportunity to add Author objects to a related set using p.authors.add(u), assuming that p is the object of Paper model, and a is an object of Author model.

  • You can access all related authors of a Paper instance using p.authors.all().
  • You can access all related papers of an Author instance using u.papers.all().

This will return an instance of QuerySet that you can operate on.


See this documentation page to learn more.