0

In a layout I have a predefined number of placements/spaces.

I want to populate this with images, but there are cases where there is no image, or less:

When there are enough images, the following code is ok:

{% for placement in placement_list %}
        <a href="{{ placement.url }}">
            <img src="{{ MEDIA_URL }}{{ placement.image }}"/>
        </a>
 {% endfor %}

But when are zero or less images than available space in layout, I want to show a default image/

I know that the length I can check with {{ placement_list|length }}

I want to have something like:

 for i to (MAX_NR_placements - {{ placement_list|length }})
       <img src="default" />
user3541631
  • 3,686
  • 8
  • 48
  • 115
  • 1
    Could you possibly pad your `placement_list` to the correct length within your view before sending it to the template? You could set the `image` attribute of those pad entries to your default, then your for-loop in the template would Just Work. – souldeux Nov 04 '18 at 17:00
  • placement_list actually is a queryset in the view, which contains Model objects with multiple attributes like image, url. The default is just an image, modifying the queryset as list and creating new objects, I don't see it as the 'best' option – user3541631 Nov 04 '18 at 17:03
  • There is no numeric for-loop in the Django templating language by design; this logic really, reeeeally should be completed in the view. You can back your way into it like this, but it's inelegant at best: https://stackoverflow.com/questions/1107737/numeric-for-loop-in-django-templates – souldeux Nov 04 '18 at 17:11

1 Answers1

1

I hate myself for being that StackOverflow user who "answers" a question by saying "don't do it that way, do it this way" but ... don't do it that way, do it this way:

Django really encourages you to do this sort of logic in the view. You "should" be padding your placement_list to the correct length before sending it to your template for rendering.

You said that placement_list is currently a queryset defined in the view and then sent as-is to the template. Let's say that the way you're currently doing that looks vaguely like this:

placement_list = MyModel.objects.all()

What if we want this to always be at least 50 items long? We can convert placement_list from a queryset to a list and add dummy MyModel objects to that array.

padded_placements = placement_list + [MyModel()]*(50-len(placement_list))

Note that we're instantiating MyModel() objects here, but not actually doing a Django-style create and/or saving anything to the database. These are empty "dummy" objects that we can send to our template to use as placeholders without polluting our real data.

Let's make one tweak to the line we just wrote, to make templating easier:

padded_placements = placement_list + [MyModel(url='DEFAULT')]*(50-len(placement_list))

Now all of our dummy objects will have their url attribute set to the string DEFAULT. This wouldn't pass object-level URL validation, but that doesn't matter since we're not saving these dummies to the DB - those validators never get called.

Send padded_placements to the template and look for our custom attribute:

{% for placement in padded_placements %}
    {% if placement.url != 'DEFAULT' %}
        <a href="{{ placement.url }}">
            <img src="{{ MEDIA_URL }}{{ placement.image }}"/>
        </a>
    {% else %}
        <p>do default stuff</p>
    {% endif %}
 {% endfor %}

Two notes:

  • Your dummy MyModel objects may need additional attributes set, depending on how those objects get used in the template and how your model represents itself. For example, if your str/repr method relies on MyModel.description then you'll need to include that field.

  • Passing a list to your template instead of a queryset means that you lose access to queryset stuff within your template. That is a great thing, since any queryset-type logic (filtering, exists checks, etc) you're doing really, REALLY should be done in the view as opposed to the template.

souldeux
  • 3,615
  • 3
  • 23
  • 35
  • can work, but the new issue is that this default files are in static and in the for loop is searching all in media(uploaded by user) so I get for them a src error – user3541631 Nov 04 '18 at 19:04
  • So construct the url with STATIC_URL instead of MEDIA_URL in the default case. – souldeux Nov 04 '18 at 19:05
  • is constructed with static but in template I have src="{{ MEDIA_URL }}{{ placement.image }}" that creates the problem – user3541631 Nov 04 '18 at 19:07
  • You have that in the `if` block that handles your non-default case. Just use STATIC_URL in the `else`block. – souldeux Nov 04 '18 at 19:08
  • I didn't saw that you used the placement.url to check in which case I am – user3541631 Nov 04 '18 at 19:10