Add primary_answer field to AnatomyQuestion model and create migration to backfill existing values

This commit is contained in:
Ross
2026-02-16 09:32:25 +00:00
parent 90c96333a1
commit f72ec82525
2 changed files with 75 additions and 24 deletions
@@ -0,0 +1,43 @@
"""Add primary_answer FK to AnatomyQuestion and backfill existing values.
This migration adds a nullable FK `primary_answer` on AnatomyQuestion
pointing to Answer, then backfills it using the current logic of
selecting the first non-proposed CORRECT answer (status '2').
"""
from django.db import migrations, models
import django.db.models.deletion
def forwards(apps, schema_editor):
AnatomyQuestion = apps.get_model('anatomy', 'AnatomyQuestion')
Answer = apps.get_model('anatomy', 'Answer')
# Select first non-proposed CORRECT answer (status '2') per question
for q in AnatomyQuestion.objects.all():
ans = Answer.objects.filter(question_id=q.pk, proposed=False, status='2').first()
if ans:
q.primary_answer_id = ans.pk
q.save(update_fields=['primary_answer'])
def backwards(apps, schema_editor):
AnatomyQuestion = apps.get_model('anatomy', 'AnatomyQuestion')
for q in AnatomyQuestion.objects.all():
q.primary_answer = None
q.save(update_fields=['primary_answer'])
class Migration(migrations.Migration):
dependencies = [
('anatomy', '0025_exam_created_exam_updated'),
]
operations = [
migrations.AddField(
model_name='anatomyquestion',
name='primary_answer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_for_questions', to='anatomy.answer'),
),
migrations.RunPython(forwards, backwards),
]
+32 -24
View File
@@ -127,6 +127,15 @@ class AnatomyQuestion(QuestionBase):
related_name="anatomy_authored_questions",
)
primary_answer = models.ForeignKey(
'Answer',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='primary_for_questions',
help_text="Optional explicit primary answer for this question",
)
class Meta:
permissions = ()
@@ -151,33 +160,32 @@ class AnatomyQuestion(QuestionBase):
def get_absolute_url(self):
return reverse("anatomy:question_detail", kwargs={"pk": self.pk})
def get_primary_answer(self):
if hasattr(self, "prefetched_primary_answer") and self.prefetched_primary_answer:
ans = self.prefetched_primary_answer[0]
else:
ans = self.answers.filter(
proposed=False, status=Answer.MarkOptions.CORRECT
).first()
# ans = self.answers.first()
def get_primary_answer_obj(self):
"""Return the Answer instance considered the primary answer, or None.
Preference order:
- explicit `primary_answer` FK if set
- `prefetched_primary_answer` if provided by a queryset
- first non-proposed Answer with status CORRECT
"""
if getattr(self, "primary_answer", None):
return self.primary_answer
if hasattr(self, "prefetched_primary_answer") and self.prefetched_primary_answer:
return self.prefetched_primary_answer[0]
return self.answers.filter(proposed=False, status=Answer.MarkOptions.CORRECT).first()
def get_primary_answer(self):
"""Return the primary answer text (string) for templates and display.
Keeps backward compatibility by returning the answer string when
available, or "None yet..." if no primary answer is defined.
"""
ans = self.get_primary_answer_obj()
if ans is None:
return "None yet..."
else:
return ans.answer
if (
self.answers.filter(
proposed=False, status=Answer.MarkOptions.CORRECT
).count()
> 0
):
return (
self.answers.filter(proposed=False, status=Answer.MarkOptions.CORRECT)
.first()
.answer
)
else:
return "None yet..."
return ans.answer
def get_exams(self):
e = self.exams.all().values_list("name", flat=True)