Jekson
Jekson

Reputation: 3262

How to display relationship with back reference at django template through User?

I have such models

class Employee(models.Model):
    """Employee information."""
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='employee', unique=True)
    position = models.CharField("current position in a company", max_length=64, blank=True)
    birth_date = models.DateField("date of birth", null=True)
    skills = models.ManyToManyField(
        Technology, through="Skill", verbose_name="skills", blank=True)


class Technology(models.Model):
    """Technologies."""
    name = models.CharField('technology name', max_length=32, unique=True)

class Skill(models.Model):
    """Information about an employee's skills."""
    LEVELS = (
        ('basic', 'Basic'),
        ('intermediate', 'Intermediate'),
        ('advanced', 'Advanced'),
        ('expert', 'Expert'),
    )
    employee = models.ForeignKey(
        Employee, on_delete=models.CASCADE, related_name="employee_skills")
    technology = models.ForeignKey(Technology, on_delete=models.CASCADE)
    start_date = models.DateField(
        verbose_name='Works with since:')
    level = models.CharField("level", max_length=64, choices=LEVELS)

And I can't understand why my template code doesn't work

template.html

{{ user.employee.position }}
{{ user.employee.birth_date }}
{{ user.employee.summary }}

{% for i in  user.employee.skills.all %}
{{ i.technology.name }}
{{ i.level }}
{% endfor %}

I can't see absolutely nothing. All models possible to see at adminpanel. And else then I using TemplateView such as

class AccountView(TemplateView):

    template_name = "profile.html"

    def get_context_data(self, **kwargs):
        context = super(AccountView, self).get_context_data(**kwargs)      
        context['skills'] = 
        Skill.objects.filter(employee__user=self.request.user)

        return context

then everything works fine.

Upvotes: 2

Views: 1199

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477190

There is something wrong with the modeling. You should use a OneToOneField between Employee and User. In essence an OneToOneField is a unique ForeignKey. It will however change some logic, such that user.employee will access the related Employee object, not a QuerySet of Employees:

class Employee(models.Model):
    # ...
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='employee'
    )
    # ...

In your AccountView, you unified 'skills' with the skills of that employee, indeed:

class AccountView(TemplateView):

    template_name = "profile.html"

    def get_context_data(self, **kwargs):
        context = super(AccountView, self).get_context_data(**kwargs)      
        context.update(
            skills=Skill.objects.filter(employee__user=self.request.user).select_related('technology')
        )
        return context

You might want to use .select_related(..) here to prevent the so-called "N+1 problem" where for each skill, you will make an extra query.

So you can render the skills with:

{% for skill in skills %}
    {{ skill.technology.name }}
    {{ skill.level }}
{% endfor %}

or you can access is through:

{% for skill in request.user.employee.employee_skills.all %}
    {{ skill.technology.name }}
    {{ skill.level }}
{% endfor %}

although the above is less safe, since it is possible that a User has no related Employee object.

Upvotes: 2

Sayse
Sayse

Reputation: 43320

Your Employee model currently has a one to many relationship with a user

class Employee(models.Model):
    """Employee information."""
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='employee', unique=True)

according to your comment you need a one to one relation ship so you need to change this to use a OneToOneField instead of a ForeignKey

Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.

Upvotes: 2

Related Questions