Abdoulaye
Abdoulaye

Reputation: 107

Django Model and Form for Comment system

I have made a comment system under my books where only the authenticated user can comment. When I use the form to add a comment, it doesn't work! why ?

here is my models

models.py

class Books(models.Model):
    author = models.ManyToManyField(Authors)
    title = models.CharField(max_length=250)
    number_of_pages = models.PositiveIntegerField(validators=[MaxValueValidator(99999999999)])
    date_added = models.DateField(auto_now_add=True)
    updated = models.DateField(auto_now=True)
    publication_date = models.PositiveIntegerField(default=current_year(), validators=[MinValueValidator(300),
                                                                                       max_value_current_year])
    cover = models.ImageField(upload_to='pics/covers/', default='pics/default-cover.jpg')
    pdf_file = models.FileField(upload_to='pdfs/books/', default='pdfs/default-pdf.pdf')
    category = models.ForeignKey(Categories, on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Comments(models.Model):
    book = models.ForeignKey(Books, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return '{} - {}'.format(self.livre.title, self.user)

here is my forms

forms.py

class BookForm(ModelForm):
    class Meta:
        model = Books
        fields = '__all__'


class CommentForm(ModelForm):
    class Meta:
        model = Comments
        fields = ['body']

here is my views

views.py

@login_required(login_url='login')
def book_detail_view(request, book_id):
    books = get_object_or_404(Books, pk=book_id)

    context = {'books': books,}

    return render(request, 'book_detail.html', context)

@login_required(login_url='login')
def add_comment(request, comment_id):
    form = CommentForm()
    books = get_object_or_404(Books, pk=comment_id)
    user = request.user

    if request.method == "POST":
        form = CommentForm(request.POST, instance=books)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.user = user
            comment.books = books
            comment.save()
            return redirect('book_detail', books.id)

    context = {'form': form}

    return render(request, 'comment_form.html', context)

here is my book detail page

book_detail.html

{% extends 'base.html' %}

{% block title %} {{ books.title }} {% endblock %}

{% block content %}


<div class="row">
    <div class="col-lg-4">
        <p><img src="{{ books.cover.url }}"></p>
    </div>
    <div class="col-lg-8">
        <h2>{{ books.title }}</h2>
        <b>Author : </b>
        {% for author in books.author.all %}

            <a href="{% url 'detail_author' author.id %}">{{ author.name }}</a>
            {% if not forloop.last %},{% endif %}

        {% endfor %}<br/>

        <b>Catégory : </b>{{ books.category }}<br/>
        <b>Pages : </b>{{ books.number_of_pages }}<br/>
        <b>Publication : </b>{{ books.publication_date }}<br/>
        <b>Date added : </b>{{ books.date_added }}<br/>
        <b>Updated : </b>{{ books.updated }}<br/>
    </div>
</div>


<div class="row">
    <div class="col-lg-12">
        <p><a href="{{ books.pdf_file.url }}"><button class="btn btn-outline-dark btn-sm"><i class="far fa-eye"></i> Read</button></a></p>
    </div>
</div>

<hr/>

<div class="container-fluid">

    <h2>Comments</h2>

</div>

<div class="container-fluid">

    {% if not books.comments.all %}
        <p>No comments yet ! <a class="text-primary" href="{% url 'add_comment' books.id %}">Add comment...</a></p>

    {% else %}
        <a class="text-primary" href="{% url 'add_comment' books.id %}">Add comment !</a><br/><br/>
        {% for comment in books.comments.all%}
            <b>{{ comment.user }}</b> - <span class="text-muted" style="font-size: 13px;">{{ comment.date }}</span>
            <p>{{ comment.body }}</p>
        {% endfor %}

    {% endif %}

</div>

{% endblock %}

here is my form for comment model

comment_form.html

{% extends 'base.html' %}

{% block title %} Add a comment {% endblock %}

{% block content %}

    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Add this comment</button>
    </form>

{% endblock %}

here is my urls

urls.py

urlpatterns = [
    # BOOKS
    path('book/<int:book_id>/', views.book_detail_view, name='book_detail'),

    # COMMENTS
    path('book/<int:comment_id>/comment/', views.add_comment, name='add_comment'),

]

Upvotes: 1

Views: 338

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

Your form is currently set to edit the book, not the comment, you should remove the instance=books:

if request.method == "POST":
    #        no instance=books ↓
    form = CommentForm(request.POST)

If you use instance=books, the form will set attributes to the Books object, and then the comment = form.save(commit=False) will result in the fact that comment is a Books object that is already saved and thus updated in the database.

You also made a typo when setting the book of a Comments object: it is book, not books:

if form.is_valid():
    comment = form.save(commit=False)
    comment.user = user
    comment.book = books  # ← .book, not .books
    comment.save()

Note: normally a Django model is given a singular name, so Book instead of Books.

Upvotes: 3

Related Questions