Search code examples
python-3.xbootstrap-4enterprisedjango-2.1

Django ListView loop through info created by a class property (not from the database)


On my ListView page, I need to be able to loop through a list of statuses that come from my views.py.

I can do it on the DetailView, but I am struggling with the same process on the ListView.

I have searched and searched and can not seem to find help with my specific problem.

Working code for DetailView:

managers.py

class ICCRequestManager:

...

@property
    def statuses(self):
        """ Current status of ICC request """

        if not self.is_submitted:
            yield "Created, not yet submitted"

        if self.is_submitted and not self.legal_signoff_status:
            yield "Waiting for security review"

        if self.is_submitted and not self.security_signoff_status:
            yield "Waiting for legal review"

        if self.legal_signoff_status and self.security_signoff_status:
            yield "Fully reviewed, ICC team is working"

views.py

from . import managers

...

class ICCRequestMixin:
    """ Mixin for the request model """

    model = models.ICCRequest
    context_object_name = 'icc_request'

class ICCRequestDetailView(LoginRequiredMixin, ICCRequestMixin, DetailView):
    """ View to see the current status of an ICC request """

    template_name = "icc/request_status.html"

    def get_context_data(self, **kwargs):
        """ Add required context """

        context = super().get_context_data(**kwargs)
        context["icc_request_manager"] = managers.ICCRequestManager(self.object)

        return context

request_status.html

    <ul>
        {% for value in icc_request_manager.statuses %}
            <li>{{ value }}</li>
        {% endfor %}
    </ul>

The above displays a simple bulleted list as expected.

Problem code:

views.py

class ICCRequestListView(LoginRequiredMixin, ICCRequestMixin, ListView):
    """ View to list requests """

    template_name = "icc/request_list.html"
    paginate_by = 10

    def get_context_data(self, **kwargs):
        """ Add required context """

        context = super().get_context_data(**kwargs)
        context["icc_request_manager"] = managers.ICCRequestManager(self.object_list)

        return context

Working portion: request_list.html

<table class="table table-striped table-bordered">

    <tr>
        <th scope="col">ICC #</th>
        <th scope="col">Company</th>
        <th scope="col">Brief Description</th>
        <th scope="col">LOB</th>
        <th scope="col">Requester</th>
        <th scope="col">Status</th>
    </tr>

    {% for request in icc_request %}
    <tr>
        <th scope="row"><a href="{% url 'icc:request-detail' pk=request.id %}">{{ request.id }}</a></th>
        <td>{{ request.company_name}}</td>
        <td>{{ request.summary }}</td>
        <td>{{ request.owner_area }}</td>
        <td>{{ request.requested_on_behalf_of }}</td>
        <!--- I want the statuses list here --->
    </tr>
</table>

Attempt to loop through like on the detail page:

...
<!--- I want the statuses list here --->
<td>
            <ul>
                {% for status in request.icc_request_manager.statuses %}
                <li>
                    {{ status }}
                </li>
                {% endfor %}
            </ul>
        </td>
        {% endfor %}
    </tr>
</table>

This does not give an error, but it does not display anything (while the request_status.html page does).

Only error is the following linting error on the get_context_data:

"Parameters differ from overridden 'get_context_data' method."


Solution

  • So I figured it out.

    1) get_context_data on the ListView has nothing to do with it. Not needed.

    2) I needed to change from yields to returning a list:

        def statuses(self):
            """ Current status of icc request """
    
            statuses = list()
    
            if not self.is_submitted:
                statuses.append("Created, not yet submitted")
            if self.is_submitted and not self.legal_signoff_status:
                statuses.append("Waiting for security sign off")
            if self.is_submitted and not self.security_signoff_status:
                statuses.append("Waiting for security review")
            if self.legal_signoff_status and self.security_signoff_status:
                statuses.append("Fully reviewed, ICC team is working")
            return statuses
    

    3) Added a method to the model:

        @property
        def get_icc_request_manager(self):
            """ Get the manager for this one request """
            # Needed to import managers inside the method to prevent Circular import problem
            import icc.managers
    
            mgr = icc.managers.ICCRequestManager(icc_request=self)
            return mgr
    

    4) Used with:

    <table class="table table-striped table-bordered">
    
        <tr>
            <th scope="col">ICC #</th>
            <th scope="col">Company</th>
            <th scope="col">Brief Description</th>
            <th scope="col">LOB</th>
            <th scope="col">Requester</th>
            <th scope="col">Status</th>
        </tr>
    
        {% for request in icc_request %}
            {% with request_manager=request.get_icc_request_manager %}
            <tr>
                <th scope="row"><a href="{% url 'icc:request-detail' pk=request.id %}">{{ request.id }}</a></th>
                <td>{{ request.company_name}}</td>
                <td>{{ request.summary }}</td>
                <td>{{ request.owner_area }}</td>
                <td>
                    {% if request.requested_on_behalf_of %}
                        {{ request.requested_on_behalf_of }}
                    {% elif request.requested_by %}
                        {{ request.requested_by }}
                    {% else %}
                    &nbsp;
                    {% endif %}
                </td>
    
                <td>
                    <ul>
                        {% for status in request_manager.statuses %}
                        <li>
                            {{ status }} test 
                        </li>
                        {% endfor %}
                    </ul>
                </td>
                {% endwith %}
            {% endfor %}
        </tr>
    </table>