nsrrendra
nsrrendra

Reputation: 11

Why Error when running code for show similar post with taggit in django 3?

I'm currently learning django by following the book Django by example from antonio mele and have reached the stage of how to display the same post using tags.

I feel that I have followed the contents of the book well, but still stuck here Error in web browser

It happened with the code I made like this:

coding in models.py

from django.db import models

# Create your models here.
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from taggit.managers import TaggableManager

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager,self).get_queryset()\
                      .filter(status='published')

class Post(models.Model) : 
    STATUS_CHOICES = (
        ('draft', 'Draft'),
        ('published', 'Published')
    )
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique_for_date= 'publish')
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name= 'blog_post')
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')

    objects = models.Manager()
    published = PublishedManager()
    tags = TaggableManager()

    class Meta : 
        ordering = ('-publish',)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog:post_detail', 
                        args = [self.publish.year, 
                                self.publish.month, 
                                self.publish.day, 
                                self.slug])

class Comment(models.Model):
    post = models.ForeignKey(Post, 
                            on_delete=models.CASCADE,
                            related_name='comments')
    name = models.CharField(max_length=80)                            
    email = models.EmailField()
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    active = models.BooleanField(default=True)

    class Meta:
        ordering = ('created',)
    
    def __str__(self):
        return f'Comment by {self.name} on {self.post}'


Views.py

from django.db.models import Count
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator, EmptyPage, \
                                    PageNotAnInteger
from django.views.generic import ListView
from django.core.mail import send_mail
from .models import Post, Comment
from .forms import EmailPostForm, CommentForm
from taggit.models import Tag

# Create your views here.
def post_list(request, tag_slug=None):
    object_list = Post.published.all()
    tag = None

    if tag_slug:
        tag = get_object_or_404(Tag, slug=tag_slug)
        object_list = object_list.filter(tags__in=[tag])

    paginator = Paginator(object_list, 3) 
    page = request.GET.get('page')
    try:
        posts = paginator.page(page)
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 
                    'blog/post/list.html', 
                    {'page' : page, 
                    'posts' : posts, 
                    'tag': tag})

class PostListView(ListView):
    queryset = Post.published.all()
    context_object_name = 'posts'
    paginate_by = 3
    template_name = 'blog/post/list.html'

def post_detail(request, year, month, day, post):
    post = get_object_or_404(Post, slug=post, 
                                    status='published', 
                                    publish__year = year, 
                                    publish__month = month, 
                                    publish__day = day)

    # Menampilkan comment yang aktif di postingan ini
    comments = post.comments.filter(active=True)

    new_comment = None

    if request.method == 'POST':
        # A comment was posted
        comment_form = CommentForm(data=request.POST)

        if comment_form.is_valid():
            #buat objek comment tapi gak di simpan
            new_comment = comment_form.save(commit=False)
            
            # kasih tahu kalau postingan sekarang lg di comment
            new_comment.post = post

            # simpan comment di database
            new_comment.save()
    else:
        comment_form = CommentForm()

    # daftar postingan yang mirip
    post_tags_ids = post.tags.values_list('id', flat=True)
    similar_posts = Post.published.filter(tags__in=post_tags_ids)\
                                  .exclude(id=post.id)
    similar_posts = similar_posts.annotate(same_tags=Count('tags'))\
                                .order_by('-same_tags','-publish')[:4]

    return render(request, 
                    'blog/post/detail.html', 
                    {'post' : post, 
                    'comments': comments, 
                    'new_comment': new_comment,
                    'comment_form': comment_form, 
                    'similar_posts': similar_posts})

def post_share(request, post_id):
    # retrieve post id
    post = get_object_or_404(Post, id = post_id, status = 'published')
    sent = False

    if request.method == 'POST':
        form = EmailPostForm(request.POST)
        if form.is_valid():
            # Form fill isi nya udah bener
            cd = form.cleaned_data
            post_url = request.build_absolute_uri(
                post.get_absolute_url()
            )
            subject = f"{cd['name']} recommendeds you read " \
                      f"{post.title}"
            message = f"Read {post.title} at {post_url}\n\n" \
                      f"{cd['name']}\'s comments: {cd['comments']}"
            send_mail(subject, message, 'admin@myblog.com', [cd['to']])
            sent = True
    else:
        form = EmailPostForm()
    return render(request, 'blog/post/share.html', {'post': post,
                                                    'form': form, 
                                                    'sent': sent})

                                

detail.html

{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}

{% block content %}
    <h1>{{ post.title }}</h1>
    <p class="date">
        Published {{ post.publish }} by {{ post.author }}
    </p>
    {{ post.body|linebreaks }}
    <p>
        <a href="{% url "blog:post_share" post.id %}">
            Share this post.
        </a>
    </p>
    <h2>Similar posts</h2>
    {% for post in similar_posts %}
        <p>
            <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
        </p>
    {% empty %}
        Gak ada postingan yang mirip
    {% endfor %}

    {%  with comments.count as total_comments %}
    <h2>
        {{ total_comments }} comment {{ total_comments|pluralize }}
    </h2>
    {% endwith %}

    {% for comment in comments %}
        <div class="comment">
            <p class="info">
                Comment {{ forloop.counter }} by {{ comment.name }}
                {{ comment.created }}
            </p>
            {{ comment.body|linebreaks }}

        </div>
    {% empty %}
        <p>There are no comments yet.</p>
    {% endfor %}
    
    {% if new_comment %}
        <h2>Your comment has been added</h2>
    {% else %}
        <h2>Add a new comment</h2>
        <form method="post">
            {{ comment_form.as_p }}
            {% csrf_token %}
            <p>
                <input type="submit" value="Add Comment">
            </p>
        </form>
    {% endif %}
{% endblock %}

I really don't know what should I change, because I think it's according to what's in the book. So, please help for all friends here who know what is the problem with this line of code.

Thank You

Upvotes: 1

Views: 71

Answers (1)

Dauros
Dauros

Reputation: 10577

You have a name collision between blog app's Post model's table name: blog_post and the related field author's related_name='blog_post' because you forgot to use plural form (missing the 's' letter after blog_post). The fixed Post model:

class Post(models.Model):
    ... 
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')

Check your database whether blog_post table has a tags field. If not, you may rerun the migrations or since you are learning Django, you can start with a fresh database.

Upvotes: 0

Related Questions