Attach models to queryset in api serializer

I have a problem with extra sql requests when work with api.
My models:

class Book(models.Model):
    ...
    authors = models.ForeignKey(Authors, on_delete=models.CASCADE,related_name='book', null=True, blank=True)
    ...

class Authors(models.Model):
    author= models.ForeignKey(AuthorTable, on_delete=models.CASCADE,related_name='authors_author', null=True, blank=True)
    artist= models.ForeignKey(AuthorTable, on_delete=models.CASCADE,related_name='authors_artist', null=True, blank=True)

class AuthorTable(models.Model):
    name=models.CharField(max_length=200, null=True, blank=True)
    photo = models.ImageField(upload_to='authors/', blank=True)

My serializers

class AuthorTableSerializer(serializers.ModelSerializer):
    class Meta:
        model = AuthorTable
        fields = ['name','photo']

class AuthorsSerializer(serializers.ModelSerializer):
    author=AuthorTableSerializer()
    artist=AuthorTableSerializer()
    class Meta:
        model = Authors 
        fields = ['author','artist']
class BookSerializer(serializers.ModelSerializer):
    authors=AuthorsSerializer()
    ...
    class Meta:
        model = Book
        fields = ['authors',...]

and view

class BookList(viewsets.ModelViewSet):
    permission_classes = (permissions.IsAdminUser,) 
    queryset = Book.objects.select_related('authors'...)
    serializer_class = BookSerializer

My problem is that when I get an API request for a book, I also get a double sql query for the AuthorTable model, and while I can set Book.objects.select_related(‘authros’), I can’t set Book.objects.select_related( 'autortable)
In other words. I don’t know how to attach a external model to the book’s query that is not directly related to it.

Hi Kimiyori,

I’m not sure what you mean by double query - can you please elaborate more on that?

As for the models, I would do it slightly differently.

class Book(models.Model):
    ...
    authors = models.ForeignKey(Authors, on_delete=models.CASCADE,related_name='book', null=True, blank=True)
    ...

class Authors(models.Model):
    person = models.ForeignKey(
      AuthorTable, on_delete=models.CASCADE,related_name='authors', null=True, blank=True
    )
    # I would use the related name authors. This means you can get the related queryset from
    # the AuthorTable by using authortable_obj_variable.authors.all(). It's just a little
    # bit less verbose and just my opinion on how I like to do it.

class AuthorTable(models.Model):
      class Types(models.TextChoices):
        ART = "ART", "Artist"
        AUT = "AUT", "Author"
        
    name=models.CharField(max_length=200, null=True, blank=True)
    photo = models.ImageField(upload_to='authors/', blank=True)
    type = models.CharField(max_length=50, choices=Types.choices, default=Types.MC)

This way you only require one FK to the AuthorTable and you use the Types to determine whether the person is an artist or author. You can also adjust it slightly so that someone can be an artist and an author

Your serializers look good to me, but using prefetch_related and select_related with serializers requires a little more work. Checkout this blog entry: Optimizing slow Django REST Framework performance

You now need to add the “type” attribute to your AuthorTable serializer.

class AuthorTableSerializer(serializers.ModelSerializer):
    class Meta:
        model = AuthorTable
        fields = ['name','photo', "type"]

I suspect by removing the the second FK attribute to the AuthorTable might solve what you describe as a double query, but without further explanation, I can’t be certain.