The Template Layer

“The template layer provides a designer-friendly syntax for rendering the information to be presented to the user.”

Django has a built in function to search by default for html files and templates in a directory named “templates”, so we create one in the app directory, and we store all the html files for the project

website/
   templates/
       balance.html
       bid.html
       home.html
       index.html
       products.html
       register.html
   admin.py
   apps.py
   models.py
   tests.py
   urls.py
   views.py

Whenever we want to inject ,in a specific location, a specific file or function, action or url, we can use specific django formats :

Variables

A variable outputs a value from the context, which is a dict-like object mapping keys to values.

Variables are surrounded by {{ and }} like this:

My name is {{ user.name }}

Tags

Tags provide arbitrary logic in the rendering process.

This definition is deliberately vague. For example, a tag can output content, serve as a control structure e.g. an “if” statement or a “for” loop, grab content from a database, or even enable access to other template tags.

Tags are surrounded by {% and %} like this:

{% extends "base_template.html" %} : indicates that the specific template/part of code extends another template/part of code.

{% include "some_template.html" %} : indicates that in the specific part of the code, another template/code will be included.

{% block label %} block {% endblock %} : we use this format to enclose parts of the code we want to be replaced or included, when a specific request/event is triggered.

if/else if/else statements

{% if 'statement' %}

…code…

{% elif %}

…code…

{% else %}

…code…

{% endif %}

For loops

{% for ... %}

…code…

{% endfor %}

{% url 'website:index' %} : page redirect (views name)

{% now "Y-m-d-H-i-s" as todays_date %} : declares a new variable that holds the current date/time.

Static files

Websites generally need to serve additional files such as images, JavaScript, or CSS. In Django, we refer to these files as “static files”.

Create a new directory called ‘static’ in the app directory

website/
   migrations/
   templates/
   static/
       css/ <- Here we save the css files
       images/ <- here the images

Now that we added the static files we also have to make a directory for the file the admin uploads (for creating auctions, the item picture).

Open the settings.py and add the following lines to the end.

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

also open the urls.py and edit the code so it will look like this:

urlpatterns = [
   path('admin/', admin.site.urls),
   path('website/', include('website.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Tag Filters

Filters the contents of the block through one or more filters. Multiple filters can be specified with pipes and filters can have arguments, just as in variable syntax.

{% if messages|length >= 100 %}
  You have lots of messages today!
{% endif %}

the length filter returns the length of the value. This works for both strings and lists. There are many filters in the Django documentation, however there are occasions where we have to calculate something more complex than the given filters. Django gives us the ability to create custom filters.

Creating custom filters

To create custom filters we have to create a directory called ‘templatetags’ in the our app and inside it we will create two files.

  • __init__.py
  • custom_tags.py : Here we will define filters we will use on our templates

The custom_tags.py will look like this:

from django import template
from ..models import User
from django.utils import timezone

register = template.Library()

@register.filter(name='search')
def search(value, id):
    for v in value:
        if v.id == id:
            return True

    return False

@register.filter(name="time_left")
def time_left(value):
    t = value - timezone.now()
    days, seconds = t.days, t.seconds
    hours = days * 24 + seconds // 3600
    minutes = (seconds % 3600) // 60
    seconds = seconds % 60
    st = str(minutes) + "m " + str(seconds) + "s"
    return st

@register.filter(name="current_price")
def current_price(value):
    current_cost = 0.20 + (value.number_of_bids * 0.20)
    current_cost = "%0.2f" % current_cost
    return current_cost

We just created three new filters

  • search : searches a list for a key

    {% if watchlist|search:auction.id%} watchlist contains auction_id {% endif %}
    
  • time_left : string representation of the auction’s time left

    {{ auction.time_ending|time_left }}
    
  • current_price : the current price of the auction item based on the number of bids

    €{{ auction|current_price }}
    

Creating the project’s templates

The Index Template

For our project, the primary template is “index.html”, which contains all the necessary code for our site’s appearance. The index page contains a navbar with its components, a dummy ad, a footer, a login modal and finally the “replacement-container” which is altered depending the content we want to show.

The replacement-container :

<div class="p-2">
    <div id="replacement">
        {% block body %}
        {% include "products.html" %}
        {% endblock %}
    </div>
</div>

Whenever we are requesting the index page by default the replacement container loads the contents of the products.html

If the user is currently logged in

<!-- If the user is logged in then include the home.html contents in the navbar -->
{% if request.session.username %}
    {% include "home.html" %}
<!-- else add login and register links -->
{% else %}
    <li class="nav-item open-modal">
        <a id="login" href="#myModal" class="nav-link trigger-btn" data-toggle="modal">Login</a>
    </li>
    <li class="nav-item">
        <a id="signup" class="nav-link" href="/website/register/">Sign up</a>
    </li>
{% endif %}

After the footer we have a modal with a login form. We will talk on how to handle forms in the next section.

The Product Template

The products template contains the auctions that are still in process and all the other auctions that will start in the future.

In this template we are going to need the custom tags we made

{% load custom_tags %}

So the product template will display all the auctions by running a loop on the auction QuerySet. Each auction will be displayed as a card and depending the starting_time and ending time the auction will be active (we will be able to bid) or inactive (just showing the starting_time). Also one more QuerySet is given as a parameter the watchlist QuerySet. On every action loop we are going to check if the auction_id exists also in the watchlist QuerySet. We are achiving this with the custom tag we created ‘search’.

<!-- If there is a watchlist parameter ... -->
{% if watchlist %}
    <!-- If the auction id is also in the watchlist QuerySet ... -->
    {% if watchlist|search:auction.id%}
        Unwatch
    {% else %}
        Watch
    {% endif %}
{% else %}
    Watch
{% endif %}

The Bid Template

The bid.html contains the page that allows users to bid on auctions. It also has a static chat for the users to communicate with each other.

The user will be able to add or remove the auction to the watchlist.

In this template we have four parameters from the view model the auction and user information one special list that contains data we created inside the bid_page view and the user’s watchlist.

The static chat is a form that makes a POST request. We will talk on how to handle forms in the next section.

The Resister Template

The register template contains a form that makes a POST request. We will talk on how to handle forms in the next section.