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)