3

I'm writing a REST API using Django and Django Rest Framework. I'm currently writing the models.

I have a model for students, a model for questions and a model for answers.

class Question(models.Model): question_text = models.CharField() class Answer(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) answer_text = models.CharField() class Student(models.Model): name = models.CharField() 

The students should be able to pick one and only one answer per question. So I was thinking of designing a student picked answer model - so that I can let the students pick through a relation - like this:

class StudentPickedAnswer(models.Model): student = models.ForeignKey(Student, on_delete=models.CASCADE) answer = models.ForeignKey(Answer, on_delete=models.CASCADE) 

I found that there is a unique_together constraint for the class Meta. But it seems to me that it can't specify relations. I would need something like this;

class Meta: unique_together = ('student', 'answer__question') 

How can I achieve that the students can only pick one question per answer? Or is picking through a model with relations a bad design?

2
  • Well the unique_together is specified at database level, and this does not know anything about relations, so no, you can not look "through" foreign keys. Commented Oct 28, 2018 at 10:11
  • @WillemVanOnsem Do you have any idea how I could do it better? I was thinking about enforcing it in the API's serializers validate methods. Commented Oct 28, 2018 at 10:33

1 Answer 1

1

Because you mentioned that students need to pick answers for questions, I think you need to have a model for your question options/choices. You can then model your studentresponse model with the uniquetogether on the (student, question)

class Question(models.Model): question_text = models.CharField() options = models.ManyToManyField(QuestionOption) class QuestionOption(models.Model): option_text = models.CharField(max_length=255) class Student(models.Model): name = models.CharField() class StudentResponse(models.Model): student = models.ForeignKey(Student) question = models.ForeignKey(Question) response_option = models.ForeignKey(QuestionOption) class Meta: unique_together = ("student", "question") 

Another approach if every question needs to have unique choices. This is similar to the polls app tutorial in the Django documentation.

class Question(models.Model): question = models.CharField(...) class QuestionOption(models.Model): question = models.ForeignKey("Question",related_name="qs_options") option = models.CharField(max_length=200) class Meta: # ensuring choices are not duplicated for a question. unique_together = ("question", "option") 
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for your help! I thought about this option, too. The only problem that I see with it is that there is no way to enforce that question points to a the question that is In fact the question for the current option, is there? What I mean is there could be Question A and Question B. And in this StudentResponse model, the question attribute could point to Question A while the response_option points to an option for Question B. Do you think there is a way to enforce integrity here?
@J.Hesters With the approach I have posted above, question options are shared between different questions, hence the manytomany. However if this is not the required behaviour and questions do not share options, then you can do it as a FK. However in that case, it will be difficult to get your required behaviour of only one answer per student per question via the model. You then have to force it via your forms and validation. I have edited and included the FK approach in the answer.
I appreciate your help and it's definitely an improvement! Thank you very much. The problem unfortunately still remains that this way in the StudentResponse corrupted data can be created by assigning a response_option that doesn't belong to the respective question. So yeah I'm working with custom permissions and the serializer's validation methods.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.