Django 1.11通用评论表单问题:无法传递Fightmma正确ID
Hey there, let's work through why your comments keep getting linked to the last Fightmma in your event—this is a classic loop variable overwriting issue, and I'll walk you through fixing it step by step!
The Core Problem
Looking at your code, here's what's going wrong:
- In your
fightcardmma_detailview, you're reassigning theformvariable inside the loop over Fightmma instances. By the time the loop finishes,formonly holds data for the last Fightmma. Your template renders this singleform, so every comment submission uses the last fight's ID. - You're also handling form submissions inside the loop, which is messy—each request can only process one form submission, so this logic would break or behave unpredictably.
Step 1: Refactor the View Logic
First, move form submission handling outside the loop, and create individual forms for each Fightmma stored in a dictionary (keyed by fight ID). Here's the updated view:
def fightcardmma_detail(request, id=None): instance = get_object_or_404(Fightcardmma, id=id) # Handle ALL form submissions first if request.method == "POST": form = CommentForm(request.POST) if form.is_valid(): c_type = form.cleaned_data.get("content_type") content_type = ContentType.objects.get(model=c_type) obj_id = form.cleaned_data.get('object_id') content_data = form.cleaned_data.get("body") # Optional: Validate the object belongs to the current event if content_type.model == 'fightmma': # Ensure the fight is part of this event get_object_or_404(Fightmma, id=obj_id, Event_Number=instance) # Create or fetch the comment new_comment, created = Comment.objects.get_or_create( author=request.user, content_type=content_type, object_id=obj_id, body=content_data, ) return HttpResponseRedirect(request.path) # Get only the Fightmma instances for this event (no need for extra filters) fightmma_list = instance.fightmma_set.all() # Create a unique form for each Fightmma, stored in a dictionary fight_forms = {} for fight in fightmma_list: initial_data = { "content_type": fight.get_content_type.model, # Use the model name, not the full object "object_id": fight.id } fight_forms[fight.id] = CommentForm(initial=initial_data) # Form for the Fightcardmma itself initial_event_form = { "content_type": instance.get_content_type.model, "object_id": instance.id } event_comment_form = CommentForm(initial=initial_event_form) # Get comments for the event event_comments = instance.comments.all() context = { "instance": instance, "fightmma_list": fightmma_list, "fight_forms": fight_forms, "event_comment_form": event_comment_form, "event_comments": event_comments, # Keep other necessary context variables here } return render(request, "fightcardmma_detail.html", context)
Step 2: Update the Template to Render Per-Fight Forms
Your template was only rendering one form—now we'll loop through each Fightmma and render its unique form. Use your existing get filter to pull the right form from the fight_forms dictionary:
{% extends 'base.html' %} {% load staticfiles %} {% load get %} <!-- Event Comment Form --> <div class="event-comment"> <h3>Comment on this event</h3> <form method="POST" action="."> {% csrf_token %} {{ event_comment_form }} <input type='submit' value='Post Event Comment' class='btn btn-default'> </form> <!-- Display event comments --> <div class="comments"> {% for comment in event_comments %} <p><strong>{{ comment.author.username }}:</strong> {{ comment.body }}</p> {% empty %} <p>No comments yet for this event.</p> {% endfor %} </div> </div> <!-- Fight-by-Fight Comments --> {% for fight in fightmma_list %} <div class="fight-section"> <h4>Fight {{ fight.Custom_Number }}: {{ fight.Left_Names_Natural_Spaces }} vs {{ fight.Right_Names_Natural_Spaces }}</h4> <!-- Display fight comments --> <div class="fight-comments"> {% for comment in fight.comments.all %} <p><strong>{{ comment.author.username }}:</strong> {{ comment.body }}</p> {% empty %} <p>No comments yet for this fight.</p> {% endfor %} </div> <!-- Fight Comment Form --> <form method="POST" action="."> {% csrf_token %} {{ fight_forms|get:fight.id }} <input type='submit' value='Post Fight Comment' class='btn btn-default'> </form> </div> {% endfor %}
Step 3: Clean Up Redundant Model Code
You have duplicate comments definitions in both Fightmma and Fightcardmma: a GenericRelation and a @property method. The GenericRelation already handles reverse queries for you—delete the @property comments methods from both models:
# Remove this from Fightmma: # @property # def comments(self): # instance = self # qs = Comment.objects.filter_by_instance(instance) # return qs # Remove this from Fightcardmma too
Step 4: (Optional) Secure the Comment Form
Make your content_type field more secure by using a ModelChoiceField instead of a CharField—this prevents users from submitting invalid model types:
# In forms.py from django.contrib.contenttypes.models import ContentType class CommentForm(forms.Form): content_type = forms.ModelChoiceField( queryset=ContentType.objects.filter(model__in=['fightmma', 'fightcardmma']), widget=forms.HiddenInput ) object_id = forms.IntegerField(widget=forms.HiddenInput) body = forms.CharField(label='', widget=forms.Textarea)
Why This Fixes It
- Each Fightmma now has its own unique form with the correct
object_idandcontent_typehidden fields. - Form submission logic is centralized, so we only process one submission per request and validate it properly.
- We've removed redundant code that was conflicting with Django's built-in generic relation handling.
Content的提问来源于stack exchange,提问作者John B

