Yopa
Yopa

Reputation: 71

Django error: 'WSGIRequest' object has no attribute 'post' for comments

I'm new to django and I'm working on a blog app and trying to enable comments for posts. When I write comments from admin page everthing works fine, but as a user I get following error: 'WSGIRequest' object has no attribute 'post'. Searching similar problems I see that mostly people switch request.Post for request.POST but I do not have that in my code, I wrote view for comment as a class not as def comment(request)… How do I solve this problem?

code from views.py:

class PostDetailView(DetailView):
    model = Post

class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = [title, summary, content, image]

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

class CommentCreateView(LoginRequiredMixin, CreateView):
    model = Comment
    fields = ['content']

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

code from models.py:

class Comment(models.Model):
    post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField()
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['created']

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

    def __str__(self):
        return 'Comment of user {}: {}'.format(self.user, self.content)
class Post(models.Model):
    title= models.CharField(max_length=100)
    summary= models.TextField(max_length=200, default='defalut text ...')
    content= models.TextField()
    image= models.ImageField(default='default.png', upload_to='post_pics')
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        img = Image.open(self.image.path)
        img.save(self.image.path)

    def __str__(self):
        return self.title

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

from post_detail.html:

{% extends "recepti/base.html" %}
{% block content %}
  <article class="media content-section">
    <img class="rounded-circle article-img" src="{{ object.author.profile.image.url }}">
    <div class="media-body">
      <div class="article-metadata">
        <a class="mr-2" href="{% url 'user-posts' object.author.username %}">{{ object.author }}</a>

        <small class="text-muted">{{ object.date_posted|date:"F d, Y" }}</small>
        <small class="">{{ object.comments.count }}</small>  <i class="fa fa-comment"></i>
        <small>0</small>  <i class="fa fa-heart"></i><br>


        {% if object.author == user %}
          <div>
            <a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}">Update</a>
            <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'post-delete' object.id %}">Delete</a>
          </div>
        {% endif %}
      </div>

      <h2 class="article-title">{{ object.title}}</h2>
      <p class="article-title">{{ object.summary}}</p>
      <img src="{{ object.image.url }}">
      <p class="article-content">{{ object.content}}</p>
      <div class="btn-group" role="group" aria-label="Basic example">
          <button type="button" class="btn btn-secondary">Like</button>
          <a class="nav-item nav-link" href="{% url 'post-comment' %}">New comment</a>

        </div>
        <hr>
        {% for comment in object.comments.all %}

        <h5>{{ comment.user }}</h5>
        <h6>{{ comment.created }}</h6>
        <p>{{ comment.content }}</p>

        {% empty %}
        <p> No comments</p>
        {% endfor %}
    </div>

  </article>

{% endblock content %}

code from urls.py:

urlpatterns = [

    path('', PostListView.as_view(), name='recepti-home'),

    path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),

    path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),

    path('post/new/', PostCreateView.as_view(), name='post-create'),

    path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),

    path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),

    path('post/comment/', CommentCreateView.as_view(), name='post-comment'),

    path('about/', views.about, name='recepti-about')

]

code from comment_form.html:

{% extends "recepti/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
    <form method="POST">
        {% csrf_token %}
        <fieldset class="form-group">
            <legend class="border-bottom mb-4">New comment</legend>
            {{ form|crispy }}
        </fieldset>
        <div class="form-group">
            <button class="btn btn-outline-info" type="submit">Submit</button>
        </div>
    </form>
</div>
{% endblock content %}

Upvotes: 0

Views: 1048

Answers (2)

Pedram
Pedram

Reputation: 3920

There are couple of little issues here; First, you need a way to specify the post which is going to have the comment in some way. You can use the primary key of post for that purpose. So you need to change the url like:

urls.py:

path('post/<int:post_pk>/comment/', CommentCreateView.as_view(), name='post-comment'),

and change the action attribute of the form as well:

template:

{% extends "recepti/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
    <form method="POST" action="{% url 'post-comment' object.pk %}">       <-------------
        {% csrf_token %}
        <fieldset class="form-group">
            <legend class="border-bottom mb-4">New comment</legend>
            {{ form|crispy }}
        </fieldset>
        <div class="form-group">
            <button class="btn btn-outline-info" type="submit">Submit</button>
        </div>
    </form>
</div>
{% endblock content %}

(based on the approach you used to render this template, you may need to replace post.pk with object.pk or something similar)

and finally, in the views.py, you can access the primary key of the post using self.kwargs:

class CommentCreateView(LoginRequiredMixin, CreateView):
    model = Comment
    fields = ['content']

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.post = self.kwargs.get('post_pk')
        return super().form_valid(form)

Upvotes: 0

DennisKot
DennisKot

Reputation: 87

I guess the "post" is related to Comment.post not to the method you are using.

So Comment consists of 4 fields (one of them is autofield) but you listed only 'comment' in your fields - add other fields or use modelform

Upvotes: 0

Related Questions