djangodjango-crispy-formsdjango-comments

Django Comment Function creates HTTP 405 Error


I tried to create a commenting function for my Django blog application and comments as such work fine but I can only add them through my admin page. However, I would like users to be able to add comments to blog posts.

Comment model in models.py:

class Comment(models.Model):
    post = models.ForeignKey('feed.Post', on_delete=models.CASCADE, related_name='comments')
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField(max_length=500)
    date_posted = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.content

I tried to add the commenting function with a Class based view in my views.py:

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

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

In my urls.py I have added the comment path as such that it uses the same path as the blog post, so the users can add comments on the same page and after the comment is posted they are still on the page of the post.

urlpatterns = [
    path('', login_required(PostListView.as_view()), name='feed-home'),
    path('user/<str:username>/', login_required(UserPostListView.as_view()), name='user-feed'),
    # Blog Post View
    path('post/<int:pk>/', login_required(PostDetailView.as_view()), name='post-detail'),
    # (NOT WORKING) Comment View
    path('post/<int:pk>/', login_required(CommentCreateView.as_view()), name='post-detail'),
    path('post/new/', login_required(PostCreateView.as_view()), name='post-create'),
    path('post/<int:pk>/update', login_required(PostUpdateView.as_view()), name='post-update'),
    path('post/<int:pk>/delete', login_required(PostDeleteView.as_view()), name='post-delete'),
    path('about/', views.about, name='feed-about'),
]

For my other forms such as login, register etc. I have used crispy forms and I thought I could do the same here so I have added a small form below the blog post in my post_detail.html

{% extends "feed/base.html" %}
{% load crispy_forms_tags %}

{% block content %}
    <!-- Post (works well!) -->
    <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-feed' object.author.username %}">{{ object.author }}</a>
            <small class="text-muted">{{ object.date_posted|date:"d. F Y" }}</small>
            {% 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>
        <img class="post-image mb-2" src="{{ post.image.url }}" alt="">
        <p class="article-content">{{ object.content }}</p>

        {% for comment in post.comments.all %}
            <div class="comment">
                <div class="date">{{ comment.date_posted }}</div>
                <strong>{{ comment.author }}</strong>
                <p>{{ comment.content|linebreaks }}</p>
            </div>
            {% empty %}
                <p>No comments here yet(remove later)</p>
        {% endfor %}

        <!-- Form for the Comments (not working yet) -->
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
            <button class="btn btn-outline-secondary" type="submit">Post</button>
            </div>
        </form>

        </div>
    </article>
{% endblock content %}

Also, the form is not displayed correctly, it only shows the button but usually by adding {{ form|crispy }} the text field(s) were added automatically.

Now when I click on the submit button I am getting a HTTP 405 error and I do not know how I can get this working..

I did some research online but could not fine anything related to my problem.


Solution

  • This url is creating the problem. why do you need the pk on form creation.

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

    and if you want to create the comment with your post-detail than you can write the logic inside post_detail_view itself like in the code below.

    def tutorial_detail_view(request, lecture_id):
    tutorial = get_object_or_404(Course, pk=lecture_id) # here we get the id and all its componenet associated with it
    comments = Comment.objects.filter(course=tutorial, reply=None).order_by('-id') # here we are filtering the comment for the relevent post and getting the latest post at the top
    
    # if the request is post than create a foprm object
    if request.method == 'POST':
        comment_form = CommentForm(request.POST or None)
    
        # if the form is valid than get the comment content and than create a comment object with the course detail, user detail and the comments detail and save it
    
        if comment_form.is_valid():
            comment = request.POST.get('comment') 
            reply_id = request.POST.get('comment_id')
            qs = None
    
            # for reply we first get the comment id
            # if there is reply than we get the reply id 
            if reply_id:
                qs = Comment.objects.get(id=reply_id)# to knw the reply of that comment by getting the reply id to the comment id
    
            comment = Comment.objects.create(course=tutorial, user=request.user, comment=comment, reply=qs)
            comment.save()
    
            return redirect('teacher-profile')
    
    
    else:
        comment_form = CommentForm()
    
    context = {
        'tutorial': tutorial,
        'comments': comments,
        'comment_form': comment_form,
    }
    
    return render(request, 'apps/lecture.html', context)
    

    This way you can user one URL to render it. Hope you get some idea from it.