Django评分评论平台提交时出现IntegrityError:concours_id不能为空
问题:提交评论时触发IntegrityError,concours_id为空
我开发了一个允许用户提交星级评分和评论的意见平台,但用户提交操作时始终触发错误。具体错误信息为:
IntegrityError at /save-comment/,(1048, "Le champ 'concours_id' ne peut être vide (null)")
涉及视图concours.views.save_comment,需排查concours_id无法正确传递导致为空的问题。
模型代码
class AvisConcours(models.Model): concours = models.ForeignKey('concours.Concours', on_delete=models.CASCADE, related_name='avis_concours') note = models.ManyToManyField('concours.Note', related_name='avis_note', blank=True,) commentaire = models.TextField(blank=True) utilisateur = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) date = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = "Avis de concours" verbose_name_plural = "Avis de concours" def __str__(self): return f"{self.concours}" + " - " + f"{self.utilisateur}"
视图代码
def save_comment(request): if request.method == 'POST': commentaire = request.POST.get('commentaire') concours_id = request.POST.get('concours') utilisateur = request.user notes_to_create = [] for note_type in NoteType.objects.all(): rating_value = request.POST.get(f'rating_{note_type.id}') if rating_value is not None: # Only create notes for non-empty ratings notes_to_create.append(Note(note_type=note_type, valeur=rating_value)) # Save bulk Note created_notes = Note.objects.bulk_create(notes_to_create) # Save AvisConcours and link notes avis_concours = AvisConcours.objects.create( concours_id=concours_id, commentaire=commentaire, utilisateur=utilisateur ) avis_concours.note.set(created_notes) # Use set() to set ManyToManyField directly return redirect('merci_commentaire') return render(request, "page_commentaire.html")
模板代码
<div class="col-lg-8 ps-xxl-5 mt-5"> {% if user.is_authenticated %} <div class="give-rating-area mb-5"> <form method="post" action="/save-comment/"> {% csrf_token %} <input type="hidden" name="concours" value="{{concours.id}}"> <!-- Début:: Utilisateur connecté --> <div class="give-rating-person"> {% if user.is_authenticated %} <div class="thumb" style="width: 80px; height: 80px; border-radius: 50%; overflow: hidden;"> {% if user.profile_image %} <img src="{{ user.profile_image.url }}" alt="{{ user.full_name }}'s profile image" style="width: 80%; height: 80%; object-fit: cover; border-radius: 80%;"> {% else %} <img src="{% static 'images/avatar.jpg' %}" alt="Default profile image" style="width: 80%; height: 80%; object-fit: cover; border-radius: 80%;"> {% endif %} </div> <div class="content"> <h6>{{ user.full_name }}</h6> </div> {% endif %} </div> <!-- Début:: Utilisateur connecté --> <br><br> {% for note_type in note_types %} <div class="give-rating-person"> <div class="content"> <h6>{{ note_type.nom }}</h6> </div> <div class='give-rating'> {% for i in "12345" reversed %} <span> <input id='str{{ i }}_{{ note_type.id }}' name='rating_{{ note_type.id }}' type='radio' value='{{ i }}'> <label for='str{{ i }}_{{ note_type.id }}'> <i class="la la-star fa-sm"></i> </label> </span> {% endfor %} </div> </div> {% endfor %} <div class="mt-4"> <textarea name="commentaire" class="form--control" placeholder="Ecrivez votre commentaire" required> </textarea> <div class="text-end"> <button type="submit" class="btn btn--base submitBtn">Soumettre </button> </div> </div> </form> </div> {% else %} <div class="give-rating-area mb-5"> <p class="text-center">Veuillez vous <a href="{% url 'connexion' %}" class=" text--base">connecter</a> d'abord pour soumettre votre avis. </p> </div> <div class="mt-3"> <ul class="pagination justify-content-end"> </ul> </div> {% endif %} <div class="customer-review-wrapper"> <div> <ul class="pagination justify-content-end me-3"> </ul> </div> <div class="review-block"> {% if avis_concours.exists %} {% for avis in avis_concours %} <div class="col-lg-12"> <div> <div class="single-slide" style="width: 100%; display: inline-block;"> <div class="review-card"> <div class="give-rating-person"> {% if user.is_authenticated %} <div class="thumb" style="width: 50px; height: 50px; overflow: hidden; border-radius: 50%; margin-right: 10px;"> <img src="{% if user.profile_image %}{{ user.profile_image.url }}{% else %}{% static 'images/avatar.jpg' %}{% endif %}" alt="Image de l'utilisateur" style="width: 100%; height: auto;"> </div> <div class="content"> <h6>{{ user.full_name }}</h6> <h6> <p class="fs--14px mt-1">Concours commenté : <span class="text--base"><strong>{{ avis.concours.nom }}</strong></span></p> </h6> </div> {% endif %} </div> <hr> <div class="col-md-12"> <div class="review-card__ratings text--base"> <div class="review-card__ratings-text"> <h6>Note globlale de l'utilisateur</h6> </div> <div class="review-card__ratings-stars"> <!-- Utilisez le filtre de modèle "rating_stars" pour afficher la moyenne des notes sous forme d'étoiles --> <p class="fs--30px text-muted"> <span style="font-weight: bold; font-size:30px"> {{ avis.average_rating }}/5</span> Basé sur les notes detaillés </p> {{ avis.average_rating|rating_stars|safe }} </div> </div> </div> <hr> <h6>Notes detaillés de l'utilisateur</h6> <div class="row"> {% if avis.note.exists %} {% for note in avis.note.all %} <div class="col-md-4"> <div class="review-card__ratings text--base"> <div class="review-card__ratings-text"> <p>{{note.note_type.nom}}</p> </div> {{ note.valeur|rating_stars|safe }} </div> </div> {% endfor %} {% else %} <div class="customer-review d-flex justify-content-center"> <h5>Aucun avis pour le moment !</h5> </div> {% endif %} </div> <hr> <h5>Commentaire de l'utilisateur</h5> <p class="review-card__des"> {{avis.commentaire}} </p> <p class="review-card__date">{{ avis.date }}</p> </div> </div> </div> </div> <br> {% endfor %} {% else %} <div class="customer-review d-flex justify-content-center"> <h5>Aucun avis pour le moment !</h5> </div> {% endif %} </div> </div> </div>
排查分析与解决方案
核心问题点
- 模板上下文缺失
concours对象:模板中使用{{concours.id}}填充隐藏字段,但如果渲染该模板的视图没有把对应的Concours实例传入上下文,这个字段值会为空,导致提交后concours_id无效。 - 视图未做参数校验:当前视图直接使用
request.POST.get('concours'),未验证值是否存在、是否为有效竞赛ID,空值传入数据库触发完整性错误。
具体修复步骤
1. 确保模板渲染时传入concours对象
检查渲染page_commentaire.html的视图,必须将目标Concours实例添加到上下文:
def comment_page(request, concours_id): concours = get_object_or_404(Concours, id=concours_id) note_types = NoteType.objects.all() avis_concours = AvisConcours.objects.filter(concours=concours) return render(request, "page_commentaire.html", { "concours": concours, "note_types": note_types, "avis_concours": avis_concours })
2. 增强视图的参数校验与安全性
修改save_comment视图,提前验证concours_id的有效性,并用实例而非ID创建AvisConcours:
def save_comment(request): if request.method == 'POST': commentaire = request.POST.get('commentaire') concours_id = request.POST.get('concours') utilisateur = request.user # 校验concours_id非空且对应竞赛存在 if not concours_id: messages.error(request, "无法识别对应的竞赛,请重试") return redirect(request.META.get('HTTP_REFERER', 'comment_page')) try: concours = Concours.objects.get(id=concours_id) except Concours.DoesNotExist: messages.error(request, "对应的竞赛不存在") return redirect(request.META.get('HTTP_REFERER', 'comment_page')) notes_to_create = [] for note_type in NoteType.objects.all(): rating_value = request.POST.get(f'rating_{note_type.id}') if rating_value is not None: notes_to_create.append(Note(note_type=note_type, valeur=rating_value)) created_notes = Note.objects.bulk_create(notes_to_create) # 使用concours实例创建,避免ID传递的潜在问题 avis_concours = AvisConcours.objects.create( concours=concours, commentaire=commentaire, utilisateur=utilisateur ) avis_concours.note.set(created_notes) return redirect('merci_commentaire') return render(request, "page_commentaire.html")
3. 模板字段添加必填验证(可选)
给隐藏字段添加required属性,强制前端提交时携带该字段:
<input type="hidden" name="concours" value="{{concours.id}}" required>
内容的提问来源于stack exchange,提问作者oue serge Gedeon
相关产品推荐
相关产品推荐

