Reputation: 57
I create a 5 star rating system using Django with Javascript and I want to user comment like this:
I want to click on the stars and then return the value that is an integer
this is my models:
class Review(models.Model):
course = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='reviews')
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
rating = models.IntegerField(null=True, validators=[MinValueValidator(1), MaxValueValidator(5)])
comment = models.TextField()
created = models.DateField(auto_now_add=True)
active = models.BooleanField(default=False)
def __str__(self):
return f'{self.first_name} {self.last_name}
my views:
@csrf_exempt
def productDetailView(request, id, slug):
product = get_object_or_404(Product, id=id, slug=slug, available=True)
new_comment = None
if request.method == 'POST':
form = ReviewForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.course = product
new_comment.rating = request.POST['rating']
new_comment.save()
else:
form = ReviewForm()
return render(request, 'shop/product_detail.html', {'product': product, 'form': form})
JS function:
$(document).ready(function(){
$('.rate .rate-item').on('click', function(){
var value = $(this).data('value');
$.ajax({
url: '{{ product.get_absolute_url }}',
type: 'POST',
data: {'rating': value},
success: function(response){
alert('Rating saved successfully!');
}
});
});
});
and my template:
<form method="post">
<div class="form-singel">
{{ form.first_name|attr:" placeholder:Fast name" }}
</div>
<div class="form-singel">
{{ form.first_name|attr:" placeholder:Last Name"}}
</div>
<div class="rate-label">Your Rating:</div>
<div class="rate">
<div data-value="1" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="2" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="3" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="4" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="5" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
</form>
<div class="form-singel">
{{ form.first_name|attr:" placeholder:Comment" }}
</div>
<div class="form-singel">
<button type="submit" class="main-btn">Post Comment</button>
</div>
How do i fix it?
Upvotes: 0
Views: 96
Reputation: 3798
You do not need AJAX to send rating value separately. Instead you can use JavaScript to update the form field rating
value (while working with star elements feature), and submit it once it is ready:
forms.py
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ['first_name', 'last_name', 'comment', 'rating', 'course']
labels = {
'rating': '',
'course': '',
}
widgets = {
'rating': forms.HiddenInput(),
'course': forms.HiddenInput(),
'first_name': forms.TextInput(attrs={'placeholder': 'First Name'}),
'last_name': forms.TextInput(attrs={'placeholder': 'Last Name'}),
'comment': forms.Textarea(attrs={'placeholder': 'Comment'}),
}
views.py
def product_detail(request, id, slug=None):
product = get_object_or_404(Product, id=id, available=True)
if request.method == 'POST':
form = ReviewForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, "Comment saved successfully")
# Probably want to redirect somewhere else
return redirect(reverse('core:product_detail', kwargs={'id': product.id}))
else:
form = ReviewForm(initial={'course': product})
return render(request, 'shop/product_detail.html', {'product': product, 'form': form})
A few notes:
snake_case
and classes in CamelCase
.id
or slug
. Since you did not share all models I am not sure if
slug
is also unique so I just used id
as usual.csrftoken
presence in your requests, specially when
modifying the database, it is a security matter. Of course it is also
possible to use it with AJAX.shop/product_detail.html:
<form method="POST" action="">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
<label for="{{ field_name.label_tag }}">{{ field.label_tag }}</label><br>
{{ field }}
</div>
{% endfor %}
<div class="rate-label">Your Rating:</div>
<div id="rate" style="display: flex; flex-flow: row nowrap;">
<div data-value="1" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="2" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="3" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="4" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="5" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
</div>
<br>
<input type="submit" value="Post Comment">
</form>
<script>
const filledStarClass = 'fas fa-star';
const emptyStarClass = 'far fa-star';
var starsCount = 0
$( document ).ready(function() {
$('#submit').prop('disabled', true);
});
$('.rate-item').on('click', function(){
var value = $(this).data('value');
$('#submit').removeAttr('disabled');
updateStars(value);
$('#id_rating').val(value)
});
function updateStars(value) {
var childDivs = $('#rate').children();
for( i=0; i< value; i++ ) {
var star = childDivs[i].children[0];
star.className = filledStarClass;
}
if (value < 5) {
for( i=value; i< childDivs.length; i++ ) {
var star = childDivs[i].children[0];
star.className = emptyStarClass;
}
}
}
</script>
On template, the main point is at $('#id_rating').val(value)
where we update the form value based on the star item data-value
.
Upvotes: 1