Samir Tendulkar
Samir Tendulkar

Reputation: 1191

Adding to comments to post: Page not found (404)

Hi Djangonauts I am new to Django please forgive me if I have silly mistakes in my code and I am currently trying to add comments to my post model

below are my models.py

class Post(models.Model):
    user = models.ForeignKey(User, related_name='posts')
    title = models.CharField(max_length=250, unique=True)
    slug = models.SlugField(allow_unicode=True, unique=True)
    message = models.TextField()

    def get_absolute_url(self):
        return reverse('posts:single', kwargs={'username': self.user.username, 'slug': self.slug}) 

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super().save(*args, **kwargs)


class Comment(models.Model):
    post = models.ForeignKey(Post, related_name='comments')
    author = models.ForeignKey(User, related_name='comments')
    text = models.TextField()

    def get_absolute_url(self):
        return reverse('posts:single', kwargs={'username': self.post.user.username,
                                               'slug': self.post.slug})

views.py 1st attempt

@login_required
def add_comment_to_post(request, slug):
    post = get_object_or_404(Post, slug=slug)
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.post = post
            comment.author = request.user
            comment.save()
            return redirect('posts:single', username=comment.author.username, slug=post.slug)

    else:
        form = CommentForm()
        return render(request, 'posts/comment_form.html', {'form': form})


@login_required
def remove_comment(request, pk):
    comment = get_object_or_404(Comment, pk=pk)
    comment.author = request.user
    post_slug = comment.post.slug
    comment.delete()
    return redirect('posts:single', username=request.user.username, slug=post_slug)

urls.py

url(r'^(?P<slug>[-\w]+)/add_comment/$', views.CommentCreate.as_view(), name='add_comment'),

Also I have the urls for FBV below they both give me the same error

url(r'^(?P<slug>[-\w]+)/add_comment/$', views.add_comment_to_post, name='add_comment'),
url(r'^(?P<pk>\d+)/remove_comment/$', views.remove_comment, name='remove_comment'),

Views.py 2nd attempt

class CommentCreate(LoginRequiredMixin, CreateView):
    model = Comment
    fields = ('text',)
    form_class = 'posts/comment_form.html'

    def form_valid(self, form, *args, **kwargs):
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        slug = self.kwargs('slug')
        print(slug)
        self.object.post = get_object_or_404(Post, slug=slug)
        self.object.save()
        return super().form_valid(form)

views.py 3rd try

@login_required
def add_comment_to_post(request, slug):
    print(slug)
    post = get_object_or_404(Post, slug=slug)
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.post = post
            comment.author = request.user
            comment.save()
            return redirect('posts:single', username=comment.author.username, slug=post.slug)

    else:
        form = CommentForm()
        return render(request, 'posts/comment_form.html', {'form': form})

also changed the urls to

url(r'^add_comment/(?P<slug>[\w-]+)/$', views.add_comment_to_post, name='add_comment'),

below is the PostDetail view and url they work perfect

class PostDetail(SelectRelatedMixin, DetailView):
    model = Post
    select_related = ('user', 'group')

    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset.filter(user__username__iexact=self.kwargs.get('username'))   
   #below is the url
 url(r'^(?P<username>[-\w]+)/(?P<slug>[-\w]+)/$', views.PostDetail.as_view(), name='single'),

I get the same error message as below in all 3 views and also with changed url

Page not found (404)
Request Method: GET
Request URL:    http://127.0.0.1:8000/posts/...
Raised by:  posts.views.PostDetail

enter image description here

Upvotes: 2

Views: 491

Answers (2)

Samir Tendulkar
Samir Tendulkar

Reputation: 1191

Yess!!! got it. There are a few bandages on my head and few cracks on the wall but I finally got it.Thanks a lot to @Bulva for guiding me in the right direction

The urls.py should be

    url(r'^(?P<username>[-\w]+)/(?P<slug>[-\w]+)/add_comment/$', views.CommentCreate.as_view(), name='add_comment'),

Since we need both the username of the person posting the comment and also the post where the comment has to be posted. The above url has both

The views.py should be as below

class CommentCreate(LoginRequiredMixin, CreateView):
    model = Comment
    form_class = CommentForm #In the second attempt above I used a template name. which is incorrect we should either use a formclass from forms.py or use "fields = text" 

    def form_valid(self, form, *args, **kwargs):
        self.object = form.save(commit=False)
        self.object.author = self.request.user #I used self.object.user instead of self.object.author. since the models call the "user" as "author". I changed this  
        slug = self.kwargs.get('slug') #I added the .get without which you get a "'dict' object is not callable" error 
        print(slug)
        self.object.post = get_object_or_404(Post, slug=slug)
        self.object.save()
        return super().form_valid(form)

Oh yeah last but not the least in your templates. below is how you add the comment button. I'm sure a lot of you smart guys won't need it. but hey doesn't hurt right!!. If you are not using bootstrap remove the "class" from the anchor tag

<a class="btn btn-primary comment_button" href="{% url 'posts:add_comment' username=post.user.username slug=post.slug %}">Add Comment</a>

Upvotes: 1

Bulva
Bulva

Reputation: 1248

So I expect you have a urls.py in your projects where you included urls.py for your app (probably named posts). Project urls.py probably will look something like this:

(r'^posts/', include('project.posts.urls'))

Then in your app urls.py you have what have you send to me:

url(r'^(?P<slug>[-\w]+)/add_comment/$', views.add_comment_to_post, name='add_comment'),
url(r'^(?P<pk>\d+)/remove_comment/$', views.remove_comment, name='remove_comment'),

I personally this changed to version where slug and primary key will be in the end of the URL, like this:

url(r'^add_comment/(?P<slug>[-\w]+)/$', views.add_comment_to_post, name='add_comment')
url(r'^remove_comment/(?P<pk>\d+)/$', views.remove_comment, name='remove_comment'),

Then your URL for adding a new comment will look like http://127.0.0.1:8000/posts/add_comment/how-to-be-a-good-developer/. The last part of the URL is your slug and you should have a Post with this slug in your database because if you useget_object_or_404 function and any of your Posts in your will not have the sended slug you will get 404.

How to very easily debug your code in Django

You probably run your Django project from console (command-line) with command python manage.py runserver. In this console is running a little development server (very same as the others application servers you probably know). If you add print() statement to your code, then the output will be send on standard output (in this case to your console where you started development server). So if you send a request on a good URL you can add print statements to easily debug problem. Try something like this:

@login_required
def add_comment_to_post(request, slug):
    print('I am here in your view')
    print('This is my slug: {}'.format(slug))
    post = get_object_or_404(Post, slug=slug)
    print('This is my post: {}'.format(post))
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.post = post
            comment.author = request.user
            comment.save()
            return redirect('posts:single', username=comment.author.username, slug=post.slug)

    else:
        form = CommentForm()
        return render(request, 'posts/comment_form.html', {'form': form})

Now if you will see in your console I am here in your view the Post request was successfully resolved and the view was running. In this time you know that yours urls.py are good.

If you will see the message This is my slug: {your slug from url} you can check the slug value and check if in your database you have Post with this slug.

The last thing to check. If you will see in This is my post: {post object} in your console you know that the Django select the right Post and the problem is in the next code of the view (which I don't expect). If you will see None object or 404 or any error in your console you probably haven't the Post with the slug you sent in your URL in the database.

If you will have questions or you will want discuss some outputs in the console you can write a comment :-)

Upvotes: 1

Related Questions