1

I have a couple models, one of which is already populated with data (book name/ chapter number/ paragraph number data), and I am implementing the feature for each user to be able to add a note per each unique book name/ chapter number/ paragraph number, which I could, but I have been stack for a couple of days trying to retrieve books with the related_name note of the current user if they have any. Here are my models:

Book model that is already populated with data.

from django.db import models


class Book(models.Model):
    day = models.CharField(max_length=128)
    book = models.CharField(max_length=128)
    chapter = models.CharField(max_length=256)
    paragraph = models.CharField(max_length=256)
    text = models.TextField()
    link = models.CharField(max_length=256)

    def __str__(self):
        return f'{self.book}_{self.chapter}.{self.paragraph} '

    class Meta:
        ordering = ['-id']
        verbose_name = "Paragraph"
        verbose_name_plural = "Paragraph"

Here is the Note model that should store the current user's note regarding a specific unique book name / chapter number / paragraph number:

from django.db import models
from django.conf import settings
from rest_framework.reverse import reverse
from paragraphs.models import Book

class Note(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='author', on_delete=models.CASCADE)
    paragraph = models.ForeignKey(Book, related_name='note', on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    text = models.TextField(default=None)

    def __str__(self):
        return f'Note on {self.paragraph}' 

    class Meta:
        ordering = ['created']

    def save(self, *args, **kwargs):
        """
        """
        options = {'text': self.text} if self.text else {}
        super(Note, self).save(*args, **kwargs)

    def get_absolute_url(self): 
        return reverse('note-detail', args=[self.id])

Here are my serializers:

Book serializer

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

Note serializer

from rest_framework import serializers
from .models import Note
from users.serializers import UserSerializer
from paragraphs.serializers import BookSerializer


class NoteSerializer(serializers.ModelSerializer):
    owner = UserSerializer(many=False, read_only=True)

    class Meta:
        model = Note
        fields = ['id', 'owner', 'paragraph', 'text', 'created']

    def to_representation(self, instance):
        self.fields['paragraph'] = BookSerializer(read_only=True)
        return super(NoteSerializer, self).to_representation(instance)

    def user(self):
        request = self.context.get('request', None)
        if request:
            return request.user
        return None

    def create(self, validated_data):
        note, _ = Note.objects.update_or_create(
            owner=self.user(),
            paragraph=validated_data.get('paragraph', None),
            defaults={'text': validated_data.get('text', None)})
        return note

The data I am getting:

{
    "id": 25,
    "day": "2",
    "book": "Some book",
    "chapter": "1",
    "paragraph": "3",
    "text": "This is an example text that the user would like to attach a note to",
    "link": "https://somelink.com",
}

The data I am trying to get:

{
    "id": 25,
    "day": "2",
    "book": "Some book",
    "chapter": "1",
    "paragraph": "3",
    "text": "This is an example text that the user would like to attach a note to",
    "link": "https://somelink.com",
    "note": "note of current user or none"
}

Any help is appreciated

coredumped0x
  • 768
  • 1
  • 12
  • 28

2 Answers2

1

models.py:

class Book(models.Model):
    day = models.CharField(max_length=128)
    book = models.CharField(max_length=128)
    chapter = models.CharField(max_length=256)
    paragraph = models.CharField(max_length=256)
    text = models.TextField()
    link = models.CharField(max_length=256)

    def __str__(self):
        return f'{self.book}_{self.chapter}.{self.paragraph} '

    class Meta:
        ordering = ['-id']

class Note(models.Model):
    owner = models.ForeignKey(to = User, related_name='author', on_delete=models.CASCADE)
    book = models.ForeignKey(Book, related_name='note', on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    text = models.TextField(default=None)
    
    def __str__(self):
        return '%s(%s)' %(self.owner,self.book)

serializers.py:

class NoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Note
        fields = ['id', 'owner', 'book', 'text', 'created']
        
class BookSerializer(serializers.ModelSerializer):
    note = serializers.StringRelatedField(many=True, read_only=True)
    # note = NoteSerializer(many=True, read_only=True)
    class Meta:
        model = Book
        fields = ['day','book','chapter','paragraph','text','link','note']

Output:

{
    "day": "2",
    "book": "some book",
    "chapter": "1",
    "paragraph": "example",
    "text": "some textttttttttttttttttttttttttttttttttttttttttttttttt",
    "link": "http://127.0.0.1:8000/admin/api/book/add/",
    "note": [
        "admin(some book_1.example )"
    ]
}

It's return all field:

class NoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Note
        fields = ['id', 'owner', 'book', 'text', 'created']
        
class BookSerializer(serializers.ModelSerializer):
    # note = serializers.StringRelatedField(many=True, read_only=True)
    note = NoteSerializer(many=True, read_only=True)
    class Meta:
        model = Book
        fields = ['day','book','chapter','paragraph','text','link','note']

Output:

{
    "day": "2",
    "book": "some book",
    "chapter": "1",
    "paragraph": "example",
    "text": "some textttttttttttttttttttttttttttttttttttttttttttttttt",
    "link": "http://127.0.0.1:8000/admin/api/book/add/",
    "note": [
        {
            "id": 2,
            "owner": 1,
            "book": 1,
            "text": "saaaaaaaaaaaaaaaaaaa",
            "created": "2021-02-24T14:34:13.279750Z"
        }
    ]
}
Pradip Kachhadiya
  • 2,067
  • 10
  • 28
0

What you're actually trying to achieve is for the NoteSerializer to include fields from the Foreign-key related book model. Overriding the to_representation method of the serializer is clunky and not the way to go. See here a better approach.

Avi Nehama
  • 103
  • 1
  • 8
  • thank you for you reply. I appreciate it. I followed the link and tried to apply what I think I understood from the post, but I am getting the following error: django.db.utils.ProgrammingError: column notes_note.book_id does not exist LINE 1: ...ELECT "notes_note"."id", "notes_note"."owner_id", "notes_not. Is there anything wrong with how I wrote my models, please? – coredumped0x Feb 23 '21 at 23:39
  • It is hard to tell what is wrong without seeing the code. Technically speaking your models seem reasonable, however from a modeling (and hence readability and maintainability) perspective they are quite confusing. In particular, having a 'book' field on a Book model does not make sense. – Avi Nehama Feb 24 '21 at 07:28
  • yes you are right, I believe the best name for the model would be 'Lesson' and it also avoids the confusion. Thank you for your help. Ah, and when you said without saying the code, could you please tell me what code do you mean? – coredumped0x Feb 24 '21 at 12:13
  • @MurphyAdam I was referring to your comment: "I followed the link and tried to apply what I think I understood from the post" – Avi Nehama Feb 24 '21 at 12:39
  • Ah, yes. I set the note = serializers.RelatedField(read_only=True) and added the depth = 1. Do you think there's a better way doing this? I feel like I am doing this in a wrong way – coredumped0x Feb 24 '21 at 12:54