Reputation: 863
I have this two following classes in my model:
class Answer(models.Model):
answer = models.CharField(max_length=300)
question = models.ForeignKey('Question', on_delete=models.CASCADE)
def __str__(self):
return "{0}, view: {1}".format(self.answer, self.answer_number)
class Vote(models.Model):
answer = models.OneToOneField(Answer, related_name="votes", on_delete=models.CASCADE)
users = models.ManyToManyField(User)
def __str__(self):
return str(self.answer.answer)[:30]
In the shell I take the first Answer:
>>> Answer.objects.all()[0]
<Answer: choix 1 , view: 0>
I want to get the Vote object:
>>> Answer.objects.all()[0].votes
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\Hippolyte\AppData\Roaming\Python\Python38\site-packages\django\db\models\fields\related_descriptors.py", line 420, in __get__
raise self.RelatedObjectDoesNotExist(
questions.models.Answer.votes.RelatedObjectDoesNotExist: Answer has no votes.
But an error occured.
I don't understand why the related_name
is not recognized. Could you help me ?
Upvotes: 7
Views: 8530
Reputation: 2909
Your related_name
IS recognized, but it is only assigned to the instance if the related object exists.
In your case, there is no Vote
instance in your database where its answer
field points to your Answer
instance
Just catch the exception and return None
if you want to proceed:
answer = Answer.objects.all().first()
try:
vote = answer.votes
except Answer._meta.model.related_field.RelatedObjectDoesNotExist as e:
vote = None
If you want to shorten this you can use hasattr(answer, 'vote')
, to check, but this will mask ALL exceptions arising from the db lookup if any
answer = Answer.objects.all().first()
vote = answer.votes if hasattr(answer, 'votes') else None
Note that since you used a OneToOneField, answer.votes
will always return a single instance if the related Vote
exists. As such, it would be more appropriate to use related_name='vote'
(without the s)
Upvotes: 14
Reputation: 94
Check if your Answer
has a Vote
by querying the Vote
model without using the OneToOne relationship
Try doing:
ans = Answer.objects.all()[0]
Vote.objects.filter(answer_id = ans.id).all()
Extra tip, you should use a singular word on related_name (vote
instead of votes
), because it is a OneToOne, meaning an Answer can only can one Vote (and vice-versa)
Upvotes: 1
Reputation: 477160
You used a OneToOneField
here. This thus means that each Vote
points to a different Anwer
object. There is thus at most one Vote
per Answer
. A query like some_answer.votes
will immedately query for that Vote
object, and if it does not exists, it will raise a RelatedObjectDoesNotExist
error (which is a subclass of a ObjectDoesNotExist
exception). So the related_name
itself is recognized, but there is no related Vote
object.
It is however suprising to use a OneToOneField
here. It means that each vote points to a unique Answer
? I think you probably want to use a ForeignKey
here, since otherwise a related_name='votes'
makes not much sense.
Upvotes: 1