Need Help Fixing Many-to-Many Query and Template Loop

Ken we have ignition and liftoff. You’re super. Thanks many times. I don’t think I ever would have figured out to add .all. Can you recommend a good resource that addresses what we did?

It’s really all documented with examples on the Many-to-many relationship page.

Thanks for the link. At any rate, I don’t know you, but your willingness to help a stranger in need speaks deeply about your character. Kowtow.

You’re much too kind.

This community is just full of people willing to share their time and talents, I’m only one of many.

Ken

1 Like

You’re the only one who has responded, so …

Hi Ken, another issue here.

I have the following view …

def browse_subject_city(request, id):
    collection = Collection.objects.filter(subject_city=id).prefetch_related('collector').all()
    subject_city = CollectionSubjectCity.objects.get(pk=id)
    template = 'collections_app/collection_browse.html'
    context = {'collection': collection,
               'subject_city': subject_city}
    return render(request, template, context)

In my template, I can’t access any of the collector attributes. Also can’t access of the collection attributes. I’ve tried everything I can think of, and I’m stumped.

{% for collector in collection.collector.all %}
                <li><a href="/collections/{{ collection.id }}">{{ collector.inst_main_name }}{% if collector.inst_sub_name %}, {% endif %}{{ collector.inst_sub_name }} {{ collector.person_last_name }}{% if collector.person_last_name %}, {% endif %}{{ collector.person_first_name }} &#8226; {{ collection.name }}</a></li>
            {% endfor %}

That’s because the variable named “collection” in the view is a queryset and not an individual collection object.

Side note #1: You don’t need the “all” in the statement:
collection = Collection.objects.filter(subject_city=id).prefetch_related('collector').all()
It’s better written as:
collection = Collection.objects.filter(subject_city=id).prefetch_related('collector')

Opinion: If I can draw a conclusion from these most recent two issues, you seem to be having a little bit of confusion around the concept of a Queryset. Taking the time to really read/review/experiment with queries in the shell, along with perhaps re-working through the appropriate tutorial page along with the appropriate examples page could go a long way toward clearing some of this up.

Thanks Ken. You’re right that I do need a tutorial on querying in general, though I’ve been able to arrive at a basic understanding. Honestly I have spent a good deal of time with the documentation, but sometimes applying it to a real project is challenging. Everything was working fine until I shifted to the many-to-many model. I understand that I’m dealing with a Queryset in this case, but I still am confused about why I can’t access attributes in the template I shared. Can you point me in the right direction? I have taken the the .all() from the query. To me, .all() says retrieve all objects, including related objects, right?

re: all() - no, all just means retrieve all objects from the table, don’t apply any filters. It’s got nothing to do with retrieving related objects - that’s the purpose of the prefetch_related clause. So using the all in combination with the filter is a bit redundant.

My first statement in the prior response is the answer to that question. In your context being rendered by your template, “collection” is a queryset - a list. So collection.collector is trying to identify an attribute named collector in a list.
So in this case, you need to iterate over the collection object and then retrieve the collector attribute from the iterator.

And again, I’m going to comment on you using “collector” as an iterator element when you have an attribute also named “collector”. You are adding to your own confusion by not making distinguished names for these different entities - if you want to get past some of these issues, you really need to stop using the same name for different things.

Ken, hearing you loud and clear, and I understand what you’re saying. Every little bit of guidance adds to my store of knowledge. Having near interactive sessions with you, rather than fishing things out of the documentation is more effective in terms of my learning style, so I appreciate you working with me.

For some reason, I’m having trouble accessing collector attributes using this statement. Logically you would work through a_collection to get collector info, but that does not seem to work here.

{% for a_collection in collection %}
                <li><a href="/collections/{{a_collection.id}}">
                    {{a_collection.collector.inst_main_name}}
                    {% if a_collection.collector.inst_sub_name %}, {% endif %}
                    {{a_collection.collector.inst_sub_name}}
                    {{a_collection.collector.person_last_name}}
                    {% if a_collection.collector.person_last_name %}, {% endif %}
                    {{a_collection.collector.person_first_name}}
                    &#8226; {{a_collection.name}}</a></li>
            {% endfor %}

assuming:

collection = Collection.objects.filter(subject_city=id).prefetch_related('collector')

is being passed into the template with the same identifier (e.g. context={'collection':collection})

Breaking this down a little step-by-step -
a_collection is a single instance of a Collection object.

so, from the model definition at the top of this thread -
a_collection.collector is the ManyToMany field. It represents the relationship of all the Collectors associated with that Collection.

this then leads to:
a_collection.collector.all being your reference to the set of the Collector objects related to this Collection

which then brings us to the need to iterate through a_collection.collector.all to retrieve the individual elements of each Collector.

So what you’ll end up with is a nested loop to handle each Collector for each Collection.

Ken

It’s coming together … I can see the light at the end of the tunnel. Thanks again for helping me during my journey.

1 Like

Ken, I’m embarrassed to come back to you, but I can’t get anything with this, though it seems to make sense:

    <ul class="body">
    {% for a_collection in collection.collector.all %}
        <li><a href="/collections/{{ a_collection.id }}">
            {{ a_collection.collector.inst_main_name }}
            {% if a_collection.collector.inst_sub_name %}, {% endif %}
            {{ a_collection.collector.inst_sub_name }}
            {{ a_collection.collector.person_last_name }}
            {% if a_collection.collector.person_last_name %}, {% endif %}
            {{ a_collection.collector.person_first_name }} &#8226; {{ a_collection.name }}</a></li>
    {% endfor %}
    </ul>

From my earlier response:

a_collection.collector.all being your reference to the set of the Collector objects related to this Collection

which then brings us to the need to iterate through a_collection.collector.all to retrieve the individual elements of each Collector .

So, if you have a collector object, how do you access the inst_main_name?

Help me get one point of confusion out the way. If I have the following view:

def browse_subject_city(request, id):
    subject_city = CollectionSubjectCity.objects.get(pk=id)
    collection = Collection.objects.filter(subject_city=id).prefetch_related('collector')
    template = 'collections_app/collection_browse.html'
    context = {'collection': collection,
               'subject_city': subject_city}
    return render(request, template, context)

Really, I understand your advice about using a_collection to help keep things less confusing, but I could still use the following as my iteration reference statement to corral the collection object and related collectors objects:

collection.collector.all

What makes sense to me is to write, as I have successfully done on the collection detail template, "{% for collector in collection.collector.all %}

I have used this before on other templates. To access collector attributes such as inst_main_name, I simply use “collector.inst_main_name.” For collection attributes, it looks like I would do, for example, “collection.name” etc., but I’m not sure.

Briefly, at every point in either a view or a template, when you are referring to the attributes of an object, you need to be aware of what the object you are referencing is - and that’s why I will continue to make comments about your choices for attribute names. It leads to confusion when having discussions like this because when you throw out the name “collector”, are you talking about the collector attribute of a collection, or are you talking about the iteration variable in your template?

If you work on making distinct names for distinct entities, some of these relationships will become clearer.

I now have “{% for a_collector in a_collection.collector.all %}”

{{ a_collector.inst_main_name }} - nothing
{{ a_collector.collector.inst_main_name }} - nothing
{{ inst_main_name }} - nothing
{{ a_collector.collection.collector.inst_main_name }} - nothing

I realize I’m flailing here, but I was trying to test any variation I could think of …

Now, just for grins, if I do " {% for collection in collection %}", I can handily access the following:

{{ collection.name }}

Which confirms that the query is working, as there are only two collectons that qualify per screenshot.

I’m going to see the entire template here - there’s too much context missing from these snippets. (And please enclose the lines of the templates between lines consisting of only three backtick characters.)

Here’s what I’m working with right now …

{% extends 'base.html' %}

{% block content %}

<div style="flex: 10 1 1000px" xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <h1>Browsing All Collections with Subject "{{ subject_city.sub_city }}"</h1>
    <div class="flex-container-body">
        <div>
        <ul class="body">
            {% for collection in collection.collector.all %} #loop through collection objects and all
                related collector objects gathered from prefect_related
                <li><a href="/collections/{{ collection.id }}"> #grab collection id
                    {{ collection.collector.inst_main_name }} #grab related collector object's field
                    "inst_main_name"
                    {% if collection.collector.inst_sub_name %}, {% endif %} #if related collector object
                    has field "inst_sub_name", add a comma
                    {{ collection.collector.inst_sub_name }} #grab related collector object's field
                    "inst_sub_name"
                    {{ collection.collector.person_last_name }} #grab related collector object's field
                    "person_last_name"
                    {% if collection.collector.person_last_name %}, {% endif %} #if related collector
                    object has field "person_last_name", add a comma
                    {{ collection.collector.person_first_name }} &#8226; {{ collection.name }}</a></li>
                #grab related collector object field "person_last_name", html entity, then collection object
                field "name"
            {% endfor %}
        </ul>
        </div>
    </div>
</div>

{% endblock %}

Assuming your view is still:

What type of object is:

{{ collection }}
?