Christopher Gunawan
Christopher Gunawan

Reputation: 93

IF conditional statement in Django template does not work

{% if user.resume %}
    <div class="empty-state-box">
        <img src="{% static 'resumes/images/resume-empty-icon.png' %}" class="centered">
        <p class="empty-state-text"><b>You currently don't have any resumes.</b><br>Create one and
            start building your very first resume</p>
    </div>
{% else %}
    <p>More information here</p>
{% endif %}

models.py

from django.db import models
from users.models import User


class Resume(models.Model):
    name = models.CharField(max_length=255, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    updated_at = models.DateTimeField(auto_now=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

views.py

from django.contrib.auth.decorators import login_required
from django.shortcuts import render


@login_required()
def my_resumes(request):
    return render(request, 'resumes/myresumes.html')

I'm trying to place an empty state if a user does not have a resume. Else, I want to show other information. I can't seem to get it to work. I think I am using the if conditional statement wrong in the template.

Upvotes: 1

Views: 4369

Answers (2)

Ian Wootten
Ian Wootten

Reputation: 141

You need to rename the backwards relationship on your resume class so you can access it using {{ user.resume }}.

By default the ForeignKey relationship you've defined from the resume to the user is accessible using user.resume_set. See the docs for more detail.

I'm guessing that you only want to have a single resume per user, so you'll probably want to use a OneToOneField instead of a ForeignKey.

class Resume(models.Model):
    name = models.CharField(max_length=255, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    updated_at = models.DateTimeField(auto_now=True)
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="resume")

    def __str__(self):
        return self.name

Additionally, your if appears to be round the wrong way, given you probably want the form to appear when a resume doesn't exist.

{% if user.resume.exists %}
    <p>More information here</p>
{% else %}
    <div class="empty-state-box">
        <img src="{% static 'resumes/images/resume-empty-icon.png' %}" class="centered">
        <p class="empty-state-text"><b>You currently don't have any resumes.</b><br>Create one and
        start building your very first resume</p>
    </div>
{% endif %}

Upvotes: 0

Antoine Pinsard
Antoine Pinsard

Reputation: 34922

First, your condition seems turned the wrong way. You code handling the case when the resume is not present is in the if user.resume statement. So it should rather be if not user.resume.

Second, the reverse relation is not named user.resume but user.resume_set. You can change this name using related_name if you want. For instance: user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='resumes') would enable to access it via user.resumes instead of user.resume_set.

Yet, if user.resume_set will always be evaluated to if True because it is a related manager, not a queryset. So you could do something like this:

{% if not user.resume_set.exists %}
    <div class="empty-state-box">
        <img src="{% static 'resumes/images/resume-empty-icon.png' %}" class="centered">
        <p class="empty-state-text"><b>You currently don't have any resumes.</b><br>Create one and
            start building your very first resume</p>
    </div>
{% else %}
    <p>More information here</p>
{% endif %}

However, in your else statement, you will likely iterate over resumes with something like this {% for resume in user.resume_set.all %}. In such, case you will hit twice the database. Once for user.resume_set.exists and once for user.resume_set.all, which is not needed because you can know the result of user.resume_set.exists from the result of user.resume_set.all. Django templates allows you to do something like this to handle empty case when iterating an object:

{% for resume in user.resume_set.all %}
    Your code to display a resume item here
{% empty %}
    <div class="empty-state-box">
        <img src="{% static 'resumes/images/resume-empty-icon.png' %}" class="centered">
        <p class="empty-state-text"><b>You currently don't have any resumes.</b><br>Create one and
            start building your very first resume</p>
    </div>
{% endif %}

But still, it is not very good practice to execute querysets in a template. To avoid this, you can pass it in your view:

@login_required()
def my_resumes(request):
    context = {'resumes': request.user.resume_set.all()}
    return render(request, 'resumes/myresumes.html', context)

Now you can do:

{% for resume in resumes %}
    Your code to display a resume item here
{% empty %}
    <div class="empty-state-box">
        <img src="{% static 'resumes/images/resume-empty-icon.png' %}" class="centered">
        <p class="empty-state-text"><b>You currently don't have any resumes.</b><br>Create one and
            start building your very first resume</p>
    </div>
{% endif %}

Note that you can also use the previous syntax, it will only hit the database once:

{% if not resumes %}
    <div class="empty-state-box">
        <img src="{% static 'resumes/images/resume-empty-icon.png' %}" class="centered">
        <p class="empty-state-text"><b>You currently don't have any resumes.</b><br>Create one and
            start building your very first resume</p>
    </div>
{% else %}
    <p>More information here</p>
    {% for resume in resumes %}
        Your code to display a resume item
    {% endfor %}
{% endif %}

Upvotes: 3

Related Questions