Home > Software design >  Got a `TypeError` when calling `Post.objects.create()`. This may be because you have a writable fiel
Got a `TypeError` when calling `Post.objects.create()`. This may be because you have a writable fiel

Time:11-08

Writing a Django app which has a post table that has a recursive relationship to itself. This means that post CAN have a parent post (allows for replies). I want to ensure that a post can have a null for the parent post attribute - this would denote the "root" post. However, when I implement the views, model and serializer for the posts, I get the following error (stack trace):

Got a `TypeError` when calling `Post.objects.create()`. This may be because you have a writable field on the serializer class that is not a valid argument to `Post.objects.create()`. You may need to make the field read-only, or override the PostSerializer.create() method to handle this correctly.
Original exception was:
 Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rest_framework/serializers.py", line 962, in create
    instance = ModelClass._default_manager.create(**validated_data)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/django/db/models/query.py", line 669, in create
    obj = self.model(**kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/django/db/models/base.py", line 564, in __init__
    _setattr(self, field.attname, val)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/django/db/models/fields/related_descriptors.py", line 606, in __set__
    raise TypeError(
TypeError: Direct assignment to the reverse side of a related set is prohibited. Use parent_post_id.set() instead.

Here's my model:

class Post(models.Model):
    """
        Post model
    """

    class PostObjects(models.Manager):
        """
            Return the Post object and all children posts
        """

        def get_queryset(self):
            return super().get_queryset().filter(id='')

    title = models.CharField(max_length=250)
    body = models.TextField()
    # Author deletion cascade deletes all of author's posts
    author = models.ForeignKey(settings.AUTH_USER_MODEL,
                               on_delete=models.CASCADE,
                               related_name='forum_posts')
    # if a post is deleted, all children posts will also be deleted
    # defines recursive many-to-one relationship
    parent_post = models.ForeignKey('self',
                                    on_delete=models.CASCADE,
                                    blank=True,
                                    null=True,
                                    related_name='parent_post_id')
    time_stamp = models.DateTimeField(default=timezone.now)

    # Default object manager
    objects = models.Manager()
    # Custom object manager for a particular object
    post_objects = PostObjects()

    def __str__(self):
        return self.title

Here's my serializer:

class PostSerializer(serializers.ModelSerializer):
    """
        Serializer for Post class
    """

    class Meta:
        model = Post
        fields = ('id', 'title', 'body', 'author', 'time_stamp')

Here's my view:

class PostList(generics.ListCreateAPIView):
    """
        Create a post or get a list of all posts within the database that are parent posts
        API endpoints that use this view:
            - /posts
            - /createPost
   """
    queryset = Post.objects.all()
    serializer_class = PostSerializer

Urls:

from django.urls import path, include
from .views import *
urlpatterns = [
    # path('', ),
    path('createPost/', PostList.as_view(), name='createPost'),
    path('createUser/', CreateUser.as_view(), name='createUser'),
    path('getUser/<int:pk>', GetUser.as_view(), name='getUser'),
    path('admin/addQuestion', GetQuestions.as_view(), name='addQuestion'),
    path('admin/addCategory', Category.as_view(), name='addCategory'),
    path('admin/deletePost/<int:pk>', PostDetail.as_view(), name='deletePost'),
    path('admin/getCategories', Category.as_view(), name='getCategories'),
    path('questionsBank/', GetQuestions.as_view(), name='questionsBank'),
    path('posts/', PostList.as_view(), name='getPosts'),
    path('posts/<int:pk>', PostDetail.as_view(), name='getPost'),
    path('userProgress/<int:pk>', UserProgress.as_view(), name='getUserProgress'),
    path('modifyUserProgress/<int:pk>', UserProgress.as_view(), name='modifyUserProgress'),
    path('deletePost/<int:pk>', PostDetail.as_view(), name='deletePost'),
    path('jobPostings/', JobPostings.as_view(), name='getJobPostings'),
]

POST request I make through API viewer to encounter error: enter image description here

CodePudding user response:

In serializers:

I think your fields should be begin and close with [] brackets.

change this:

fields = ('id', 'title', 'body', 'author', 'time_stamp')

To this:

fields = ['id', 'title', 'body', 'author', 'time_stamp']
read_only_fields = ['id'] #also add this

In views:

class PostList(generics.ListCreateAPIView): #updated here, removed ViewSets
    """
        Create a post or get a list of all posts within the database that are parent posts
        API endpoints that use this view:
            - /posts
            - /createPost
   """
    queryset = Post.objects.all()
    serializer_class = PostSerializer #removed serializers from here

In urls.py:

path('createPost/', ListCreateAPIView.as_view(), name='createPost'), #Updated here instead of PostList, added ListCreateAPIView

CodePudding user response:

The issue was with the related names that I was using. I used the related_name attribute in the foreign key parent post. So this was creating some sort of backward relation and causing my error. Getting rid of the related_name attribute (and adjusting serializer class to reflect this) solved my issue!

  • Related