Code to display Django (Reverse) related table throws error

Question:

I’m quite new to Django and practicing Models section of Django by following its official tutorial. I also created a project of my own and try to apply similar concepts.

This is my models.py;

from django.db import models

class Experience(models. Model):

    o01_position = models.CharField(max_length=50)
    o02_year_in = models.DateField(null=True)
    o03_year_out = models.DateField(null=True)
    o04_project = models.CharField(max_length=100)
    o05_company = models.CharField(max_length=50)
    o06_location = models.CharField(max_length=50)

    def __str__(self):
        return self.o01_position}

class Prjdesc(models.Model):

    o00_key = models.ForeignKey(
        Experience, on_delete=models.CASCADE)
    o07_p_desc = models.CharField(max_length=250)

    def __str__(self):
        return self.o07_p_desc

class Jobdesc(models.Model):

    o00_key = models.ForeignKey(Experience, on_delete=models.CASCADE)
    o08_job_desc = models.CharField(max_length=250)

    def __str__(self):
        return self.o08_job_desc

Now when I run below command in Python/Django shell it runs as expected with the related data.

>>> x = Experience.objects.get( pk = 2 )
>>> x
<Experience: Head Office Technical Office Manager>

Below two also work as expected:

>>> y = Prjdesc.objects.get( pk = 11 )
>>> y
<Prjdesc: Description 1>

>>> x.prjdesc_set.all()
<QuerySet [<Prjdesc: Description 1>, <Prjdesc: Description 2>]>

However this expression does not return anything although it should return its related record in Experience Class.

>>> y.experience
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Prjdesc' object has no attribute 'experience'

Could you tell me what I am missing here?

Asked By: BlueHawk77

||

Answers:

If you pay attention:

c = q.choice_set.create(choice_text='Just hacking again', votes=0)

there is a call through _set. But not at all:

q = Question.objects.get(pk=1)

followed by:

q.choice# which will throw the same error

By using the primary model, you can get the data associated with it from the secondary model. To do this, a special property (object) with the name secondary model_set is created in the primary model by default. In your case, for example:

x = Experience.objects.get(pk=1)
x.prjdesc_set.all()

That is, we get all the values of the secondary model with pk=1 of the primary one (one-to-many access).

If you need to get the value from the primary model from the secondary model, then as already mentioned:

Prjdesc.objects.get(id=1).o00_key.o01_position

In this case, get is used, that is, the value should be one, if more is expected, then filter should be applied.

Answered By: inquirer

As you mentioned in one of the comments above:

Strangely it returns this; Traceback (most recent call last): File "", line 1, in AttributeError: ‘Prjdesc’ object has no attribute ‘experience’.

You simply need to write c.o00_key not c.experience, you confused with official docs, they give their field name also as experince.

Generally, ForeignKey is created using model name in smallcase while defining field, and the related_name sets to model name as prefix and _set as suffix by default, so it will be prjdesc_set in your case or you can override it by using ForeignKey.related_name in the field.

With your current models use this:

>>> x = Experience.objects.get(pk=2)
>>> x
<Experience: Head Office Technical Office Manager>

>>> c = x.prjdesc_set.create(o07_p_desc='Description 5')
>>> c
<Prjdesc: Description 5>

>>> c.o00_key
>>> c
<Experience: Head Office Technical Office manager>

Note: Also it’s better to use f stings in the __str__() method, so in your models.py:


class Experience(models.Model):
    ...
    ...
    def __str__(self):
        return f"{self.o01_position}"

class Prjdesc(models.Model):
    ...
    ...
    def __str__(self):
        return f"{self.o07_p_desc}"

class Jobdesc(models.Model):
    ...
    ...
    def __str__(self):
        return f"{self.o08_job_desc}"
Answered By: Sunderam Dubey