0

Whenever I run my code, I get a "RelatedManager" object is not iterable error. Here is my models.py:

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.urls import reverse

class Post(models.Model):
    contents = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    replies = list()

    def __str__(self):
        return f'{self.author} - {self.pk} - {self.date_posted}'

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})

class Reply(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='replies')
    contents = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.author} - {self.date_posted}'

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})

    class Meta:
        ordering = ['-date_posted']

Here is my views.py:

from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Post
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from .forms import ReplyForm

class PostListView(ListView):
    model = Post
    template_name = 'app/home.html'
    context_object_name = 'posts'
    ordering = ['-date_posted']
    paginate_by = 15

def PostDetailView(request, **kwargs):
    template_name = 'app/post_detail.html'
    post = get_object_or_404(Post, pk=kwargs['pk'])
    replies = post.replies
    new_reply = None

    # Reply added
    if request.method == 'POST':
        reply_form = ReplyForm(data=request.POST)

        if reply_form.is_valid():
            # Create reply object but don't save to db yet
            new_reply = reply_form.save(commit=False)

            # Assign the current post the the reply
            new_reply.post = post

            # Save reply to db
            new_reply.save()
            replies.append(new_reply)

    else:
        reply_form = ReplyForm()

    return render(request, template_name, {'post':post, 'replies':replies, 'form':reply_form})

class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = ['contents']

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Post
    fields = ['contents']

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

    def test_func(self):
        post = self.get_object()
        if self.request.user == post.author:
            return True
        else:
            return False

class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = Post
    success_url = '/'

    def test_func(self):
        post = self.get_object()

        if self.request.user == post.author:
            return True
        else:
            return False


def about(request):
    return render(request, 'app/about.html', {'title':'About App'})

def n_help(request):
    return render(request, 'app/help.html', {'title':'App Help Page'})

The problem is when I try to iterate through the Post.replies list, it gives me a Related Manager instead of the list I intended it to be. Here is the variables in the Django debug page:

kwargs: {'pk':8}
new_reply: None
post: <Post: TestUser - 8 - 2022-02-06 00:12:33.255956+00:00>
replies: <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x00000262D26052E0>
reply_form: <ReplyForm bound=False, valid=False, fields=(contents)>
request: <WSGIRequest: GET '/post/8/'>
template_name: app/post_detail.html

As you can see the "replies" variable is a related manager, not a list. I tried making replies global and using a function to return it as a list, which worked but it still gave me the RelatedManager error. The only difference was the Django Debug variables looked like this:

kwargs: {'pk':8}
new_reply: None
post: <Post: TestUser - 8 - 2022-02-06 00:12:33.255956+00:00>
replies: []
reply_form: <ReplyForm bound=False, valid=False, fields=(contents)>
request: <WSGIRequest: GET '/post/8/'>
template_name: app/post_detail.html

Even though replies is now a list and that is the only thing I am iterating through, it still gives me the exact same error. I would appreciate any help given.

Keshav V.
  • 73
  • 7
  • because you named the `related_name` in the `Reply` model as `replies`, django under the hoods overrides the `replies` field, you need to either rename the related_name or the attribute name. Also in `get_absolute_url` of `Reply` model I think you are referencing incorrect view, namely `post-detail` or you need to pass `self.post_id` instead of `self.pk` in kwargs – Ersain Feb 06 '22 at 08:58

0 Answers0