Search code examples
pythondjangodjango-modelsdjango-viewsdjango-templates

How can I get a comma separated list with related posts from a many-to-many field?


I am trying to learn Django and have encountered a problem I can't figure out for the life of me. In this project I have an app called projects. In this app I have one model called Projectinfo and one called Buildingtypes.

Every project can have one or more building types. What I am trying to do is to write a view that lists out all the projects with a commaseparated list of related building types.

Here's a simplified version of my two models:

class Buildingtypes(models.Model):
    buildingtype = models.CharField(max_length=100)

    def __str__(self):
        return self.buildingtype

class ProjectInfo(models.Model):
    project_number = models.CharField(max_length=12, blank=True)
    project_name = models.CharField(max_length=120)
    buildingtypes = models.ManyToManyField(Buildingtypes, default=[1])

    def __str__(self):
        return self.project_name

I made a view that looks like this:

from django.http import HttpResponse
from django.template import loader
from .models import ProjectInfo

def index(request):
    myProjects = ProjectInfo.objects.all()
    template = loader.get_template('projects/index.html')
    context = {
        'projects': myProjects,
        }
    return HttpResponse(template.render(context, request))

And the template:

{% if projects %}
    {% for x in projects %}
        {{ x.project_number }} | {{ x.project_name }} | {{ x.buildingtype }}
    {% endfor %}
{% endif %}

Which resulted in this:

projectnumber | project name | building type
----------------------------------------------------------
101010        | project 1    | projects.Buildingtypes.None
202020        | project 2    | projects.Buildingtypes.None

Since that obviously failed I also tried this:

{% if projects %}
    {% for x in projects %}
        {{ x.project_number }} | {{ x.project_name }} | {% for y in x.buildingtypes %} {{ y.buildingtype }} {% endfor %}
    {% endfor %}
{% endif %}

Which resulted in this:

projectnumber | project name | building type
----------------------------------------------------------
101010        | project 1    | 
202020        | project 2    | 

I made a query in MySQL just to show you what I am looking for. The following query gives me the result I want:

SELECT
    project_number,
    project_name,
    GROUP_CONCAT(pb.buildingtype SEPARATOR ', ') AS buildingtypes
FROM
    projects_projectinfo AS pp
JOIN
    projects_projectinfo_buildingtypes AS ppb
ON
    pp.id = ppb.projectinfo_id
JOIN
    projects_buildingtypes AS pb
ON
    ppb.buildingtypes_id = pb.id
GROUP BY
    pp.id;

Result:

projectnumber | project name | building type
--------------------------------------------------------------------------------
101010        | project 1    | building type 1, building type 3
202020        | project 2    | building type 2, building type 5, building type 6

But how to convert this to a view?

I have made various attempts with select_related, prefetch_related, filter and what not, but I can't just can't figure it out.


Solution

  • You were pretty close, the last iteration of your template needs to be updated to be:

    {% if projects %}
        {% for x in projects %}
            {{ x.project_number }} | {{ x.project_name }} | {% for y in x.buildingtypes.all %} {{ y.buildingtype }} {% endfor %}
        {% endfor %}
    {% endif %}
    

    It does access the all() method of m2m relation loading the values for you which you can iterate over.

    if you want to read more about it: https://docs.djangoproject.com/en/5.0/ref/templates/language/#accessing-method-calls