Omid Shojaee
Omid Shojaee

Reputation: 347

How to access ManyToMany field without loop inside template

I have the following models defined for my portfolio projects:

class Technology(models.Model):
    title = models.CharField(max_length=10)

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = 'Technologies'


class Project(models.Model):
    title = models.CharField(max_length=100)
    description = HTMLField()
    technology = models.ManyToManyField(Technology)
    image = models.ImageField(upload_to='projects/')

    def __str__(self):
        return self.title

And I have this in my views.py:

def index(request):
    projects = Project.objects.all()
    context = {'projects': projects}
    return render(request, 'homepage/index.html', context)

And the following piece of HTML to display the projects:

{% for project in projects %}
<div class="col-lg-4 col-md-6 portfolio-item filter-django">
    <div class="portfolio-wrap">
        <img src="{% static 'homepage/img/portfolio/portfolio-1.jpg' %}" class="img-fluid">         
        <div class="portfolio-info">
            <h4>{{ project.title }}</h4>
            <p>FIRST TECHNOLOGY GOES HERE</p>
        </div>
    </div>
</div>
{% endfor %}

My challenge is the <p>...</p> tag because each project has multiple technologies, but I need to print the first one only so I can't have a for loop here.

I have tried {{ project.technology.[0] }} but that gives me Could not parse the remainder: '[0]' from 'project.technology.[0]'

Please assist.

Upvotes: 1

Views: 100

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21822

Django template language (DTL) does not support the various complex syntax that python does. This is done intentionally because one of the aims of DTL is to separate business logic from presentation logic. Hence {{ project.technology.[0] }} does not work. But instead all sorts of lookups can be performed using the . operator itself, meaning you can write {{ project.technology.all.0 }} (Here since all is callable it will get called by the template engine) to achieve the same effect as indexing.

But instead you should use the first [Django docs] method of the queryset instead which will give you either the first object or None (The [0] indexing can raise an exception if the resulting query does not return anything):

{% for project in projects %}
<div class="col-lg-4 col-md-6 portfolio-item filter-django">
    <div class="portfolio-wrap">
        <img src="{% static 'homepage/img/portfolio/portfolio-1.jpg' %}" class="img-fluid">         
        <div class="portfolio-info">
            <h4>{{ project.title }}</h4>
            {% with first_technology=project.technology.first %}
                {% if first_technology is not None %}
                    <p>{{ first_technology }}</p>
                {% else %}
                    <p>No technology</p>
                {% endif %}
            {% endwith %}
        </div>
    </div>
</div>
{% endfor %}

Upvotes: 1

Related Questions