Skip to content
Advertisement

problems with get_context_data in ListView (django)

I need to show in a template two models:

models.py:

class Dimension(TimeStampedModel):

    level = models.ForeignKey('Level', verbose_name=_('Level'), on_delete=models.CASCADE)
    name = models.CharField(verbose_name=('Name'), max_length=200)
    active = models.BooleanField(verbose_name=_('Active'), default=True)
    sort_order = models.PositiveIntegerField(verbose_name=_('sort order'), default=0)

    class Meta:
        verbose_name = _('Dimension')
        verbose_name_plural = _('Dimensions')
    
    def __str__(self):
        return self.name

class Subdimension(TimeStampedModel):

    dimension = models.ForeignKey('Dimension', verbose_name=_('Dimension'), on_delete=models.CASCADE)
    name = models.CharField(verbose_name=('Name'), max_length=200)
    active = models.BooleanField(verbose_name=_('Active'), default=True)
    sort_order = models.PositiveIntegerField(verbose_name=_('sort order'), default=0)

    objects = managers.SubdimensionManager()
    
    class Meta:
        verbose_name = _('Subdimension')
        verbose_name_plural = _('Subdimensions')
    
    def __str__(self):
        return self.name

and created a ListView of this

views.py

class DimensionListView(generic.ListView):
    model = models.Dimension
    template_name = 'dimension.html'
    context_object_name = 'dimensions'


    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        self.user = self.request.user
        self.level = self.get_level(pk=kwargs.get('level_pk'))         
        return super(DimensionListView, self).dispatch(request, *args, **kwargs)

    def get_level(self, pk):
        level = get_object_or_404(models.Level, pk=pk)
        return level

    def get_queryset(self):
        queryset = super(DimensionListView, self).get_queryset()
        return queryset.filter(active = True, level = self.level)

    def get_context_data(self, **kwargs):
        context = super(DimensionListView, self).get_context_data(**kwargs)
        context['subdimensions'] = models.Subdimension.objects.filter(active=True, dimension__level=self.level )
        return context


dimension_list_view = DimensionListView.as_view()

I need to created a filter of the same ‘dimension’ so that in the template show only the subdimensions of that dimension.

my template dimension.html:

{% include 'base.html'%}
{% block content %}
<div class="row">
    {% for dimension in dimensions %}
    <div class="col">
        <div class="card" style="width: 18rem;">
        <a class="">{{dimension.name}}</a>
        <div class="card-body"> 
        <ul>
        {% for subdimension in subdimensions %}
        <li>{{subdimension.name}}</li>
        {% endfor %}
        </ul>
        </div> 
        </div>
    </div> 
    {% endfor %}
</div>
{% endblock %}

but if u notice, show all the subdimensiones in all cards, not only the subdimension of these dimension.

Manager.py only returns a objects with filter active=True and order_by(‘sort_order’)

Advertisement

Answer

You can do it with inefficient rendering, but if you have 100-200 subdimensions – it should not be a problem.

{% for dimension in dimensions %}
<div class="col">
    <div class="card" style="width: 18rem;">
    <a class="">{{dimension.name}}</a>
    <div class="card-body"> 
    <ul>
    {% for subdimension in subdimensions %}
    {% if subdimension.dimension == dimension %}
    <li>{{subdimension.name}}</li>
    {% endif %}
    {% endfor %}
    </ul>
    </div> 
    </div>
</div> 
{% endfor %}

Alternatively, you can annotate all dimensions with corresponding subdimensions in the view.

# views.py
class DimensionListView(generic.ListView):
    ...
    
    def get_queryset(self):
        queryset = super(DimensionListView, self).get_queryset()
        # Save db hits with `prefetch_related`
        return queryset.filter(active = True, level = self.level).prefetch_related('subdimension_set')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        dims = list(context['dimensions'])
        for d in dims:
            d.all_subdimensions = list(d.subdimension_set.all())
        context['dimensions'] = dims
        return context

and then in template

{% for dimension in dimensions %}
<div class="col">
    <div class="card" style="width: 18rem;">
    <a class="">{{dimension.name}}</a>
    <div class="card-body"> 
    <ul>
    {% for subdimension in dimension.all_dimensions %}
    <li>{{subdimension.name}}</li>
    {% endfor %}
    </ul>
    </div> 
    </div>
</div> 
{% endfor %}

(… or switch to Jinja2 template rendering to access dimension.subdimension_set.all() directly in template)

Also you can have a look at LoginRequiredMixin instead of method_decorator(login_required)

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement