Form validation, Database not receiving data

I have a ‘ratings’ form which is used to submit ratings for an item. However, when the form is submitted, the rating will not appear in the admin area under ‘Ratings’. I have done some of my own debugged using print statements and discovered that it isn’t passing ‘if form.is_valid():’ statement, so im assuming the database isnt recieveing the data because of validation but other than that, I have struggled to get much further.

If I add a rating for an item through the form on the admin area, it will appear on the list of ratings on ratings.html.

I will show my ratings.html, forms.py, admin.py, my Rating class in models.py, and my ratings function in views.py below:

ratings.html

{% extends 'customer/base.html' %}

{% block content %}

<div class="container">
    <div class="row justify-content-center mt-3">
        <div class="col-md-6 col-sm-12 text-center">
            <h1>Item Ratings</h1>
            <h3>{{ item.name }}</h3>
        </div>
    </div>

    <div class="row justify-content-center mt-5 mb-5">
        <div class="col-md-8 col-sm-12 text-center">
            <form method="GET" action="{% url 'ratings' item.id %}">
                <div class="form-group">
                    <label for="name">Name:</label>
                    <input class="form-control" name="name" type="text" required>
                </div>
                <div class="form-group">
                    <label for="comment">Comment:</label>
                    <textarea class="form-control" name="comment" rows="4" required></textarea>
                </div>
                <div class="form-group">
                    <label for="rating">Rating:</label>
                    <select class="form-control" name="rating" required>
                        <option value="" disabled selected>Select rating</option>
                        <option value="5">5 - Excellent</option>
                        <option value="4">4 - Very Good</option>
                        <option value="3">3 - Good</option>
                        <option value="2">2 - Fair</option>
                        <option value="1">1 - Poor</option>
                    </select>
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </div>

    <div class="row justify-content-center mb-5">
        {% if ratings %}
        <div class="col-md-8 col-sm-12 text-center">
            <h3>Reviews</h3>
            <hr>
            {% for rating in ratings %}
            <div class="card mb-3">
                <div class="card-body">
                    <h5 class="card-title">{{ rating.name }} - {{ rating.get_rating_display }}</h5>
                    <p class="card-text">{{ rating.comment }}</p>
                </div>
            </div>
            {% endfor %}
        </div>
        {% endif %}
    </div>

</div>
{% endblock content %}

forms.py:

from django import forms
from .models import Rating

class RatingForm(forms.ModelForm):
    class Meta:
        model = Rating
        fields = ['name', 'comment', 'rating']

admin.py:

from django.contrib import admin
from .models import ShopItem, Category, OrderModel, Rating

admin.site.register(ShopItem)
admin.site.register(Category)
admin.site.register(OrderModel)
admin.site.register(Rating)

models.py:

class Rating(models.Model):
    item = models.ForeignKey(ShopItem, on_delete=models.CASCADE, related_name='ratings')
    name = models.CharField(max_length=100)
    comment = models.TextField()
    rating = models.PositiveSmallIntegerField(choices=(
        (5, 'Excellent'),
        (4, 'Very Good'),
        (3, 'Good'),
        (2, 'Fair'),
        (1, 'Poor')
    ))
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'{self.item.name} ({self.rating}) by {self.name}'

views.py:

def ratings(request, item_id):
    item = get_object_or_404(ShopItem, pk=item_id)
    ratings = item.ratings.all().order_by('-created_at')
    form = RatingForm()
    
    if request.method == 'POST':
        form = RatingForm(request.POST)
        if form.is_valid():
            rating = form.save(commit=False)
            rating.item = item
            rating.save()
            return redirect(
                reverse('ratings', args=[item.id])
            )
    
    context = {
        'item': item,
        'ratings': ratings,
        'form': form
    }
    return render(request, 'customer/ratings.html', context)

You are manually rendering the html input widgets instead of using Django to render the form fields. As a result, the template isn’t rendering the error messages that are being returned.

You should review the Working with forms page.

Also note that you send the form via GET, but only save on POST (thus the saving part is never reached).

That’s not an accurate statement. He’s got that part precisely correct.

Many thanks, got it sorted!

Was that meant at me?

Yes it was.

You get the form on a GET and save the form on the POST. You don’t save a form on a GET. (Yes, form was wrong - but the view was right.)

<form method="GET" action="{% url 'ratings' item.id %}">

What about this?

1 Like

Yes, that’s a good catch.