CM3035 Topic 03: Interaction through Serving HTML, CSS, and JS
Main Info
Title: Interaction through Serving HTML, CSS, and JS
Teachers: Daniel Buchan
Semester Taken: April 2022
Parent Module: cm3035: Advanced Web Development
Description
Server side templating; Frontend primer; Django Forms; Django Class Views.
Key Reading
HTML and Templating
HTML 5 Spec esp. part 4, HTML elements.
CSS
JS
Forms
Class Views
Lecture Summaries
3.1 Templating Languages
3.102 HTML Refresher
Markup is a means of annotating documents for describing its structure for humans or computers to parse.
We need to start by declaring what markup standard is being followed. In html5, this can be as simple as <!DOCTYPE html>
.
Markup of the remaining document is achieved by encosing regions with paired elements of tags. The first parent for the whole document is the <html>
tag.
We then have two siblings <head>
and <body>
, which are children of the html tag.
The head region contains metadata about the document, The body contains the actual display content. The metadata can include links to related files like css and JS files.
Metadata will typically be in meta
tags with the form <meta name="<metadata key>" content="<metadata value>">
and some bespoke ones like <meta charset="UTF-8">
Shows using some div tags to structure a document body.
3.104 Templating
Often we have portions of HTML pages that are re-used across a site. We want to write these once and then reuse them.
Only dynamic pieces of a page should be written for a page where it’s tied to the data.
We can create small sub-templates in Django’s system.
He creates a header.html and footer.html with the static sections. note that these have the opening and closing html and body tags in different files, they’re not nested, rather sequential.
He then creates a base.html to create the shell, including the header and other sub-templates like this:
{% include "./header.html %}
{% include "./title.html %}
<div class="content">
{% block content %}No content found {% endblock %}
</div>
{% include "./footer.html" %}
The block here is the dynamic bit.
Now if we want to use this base we can do it as follows:
{% extends "base.html" %}
{% block content %}
... the dynamic content
{% endblock %}
3.106 Template Language
A templating language provides a means to combine document content with code and small pieces of logic to generate documents dynamically.
The syntax for the Django templating language is pretty simple: {{ }}
for interpolating values and rendering them as strings. {% %}
for template macros.
Code blocks have to end with declared closures (like endif or endfor) since we can’t rely on indentation.
A with
block declares a variable binding, and can be ended with endwith
. Use sparingly though.
Keep code in templates very lightweight.
You can create custom template macros. Shows an example to generate the current date.
Creates a function to do that in a new package folder:
import datetime
from django import template
register = template.Library()
@register.simple_tag
def todays_date():
return datetime.datetime.now().strftime("%d %b, %Y")
Then we have to load the macro file in the template where we want to use it with {% load my_tags%}
3.2 CSS
We’ll keep our styles in a separate css file. That file itself is a static asset, not dynamically created.
We’ll usually have a folder to store such assets, called static
and then within that have folders for eg css
js
or images
etc.
We’ll then load them into the template with <link rel="stylesheet" type="text/css" href="../static/css/style.css">
Walks through some basic styling, most of it is follow-along coding style.
3.205 digs into the actual rules of cascade and inheritance.
Under simple inheritance, child elements inherit the style of their parents. Not every properties are subject to inheritance. Nearly 80 or so are since CSS3.
There are also precedence rules: rules declared later override those declared earlier, rules that are more specific override more general ones.
Rules for child elements override those applied to their parents.
Digs into the specificity rules a bit more (id > class > element).
3.3 JavaScript
3.302 is a skippable primer on JS. Introduces the <script>
tag. Introduces very basic syntax from scratch.
Spends a strangely long time on alert
before telling you not to use it.
Introduces control flow, including forEach
and anonymous functions (not using the arrow syntax for some reason).
Nothing new here that hasn’t been covered several times before.
3.304 Functions 101, then event handlers 101. Not great intros, nothing new from prior modules.
3.305 Introduces the DOM and its JavaScript API. Covers finding elements, setting html with innerHTML
.
3.306 Bootstrap Adds Bootstrap to the demo application.
We can do this by adding the relevant external resources (from their CDN) to the base header html template.
Or we can serve the Bootstrap files ourselves.
There is a django-boostrap4
plugin you can install. Then add it to the installed apps in the main settings file.
Doing it that way means you can then load it into the templates with {% load bootstrap4 %}
. You need to do this everywhere you want to use the bootstrap template tags (macros).
We load the bootstrap css with {% bootstrap_css %}
and js with {% boostrap_javascript %}
.
Presents the bootstrap grid and re-models the templates to be bootstrappy.
3.4 Django Forms
3.402: The Form class
Walks through creating a form using the Django form class.
It works much like a model, create a forms.py
file for your forms if you’ll have several, then from django import forms
.
Each form is a class, inheriting from forms.Form
. You specify the fields in the form, which map to the fields in the Model. You set it’s type as eg forms.Charfield
which will then provide validation.
Then within the view (the controller) you can handle the form like this (lightly adapted from the docs) :
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
from .models import Name
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
name = Name()
name.first_name = form.cleaned_data['first_name']
name.second_name = form.cleaned_data['second_name']
name.save()
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
The html template needs to include a csrf_token
as follows:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
3.404 Using Django ModelForms
Django also offers a ModelForm
class if you just want to tightly couple your form to one of your models.
Then the creation of the form itself is just a matter of:
class GeneForm(ModelForm):
class Meta:
model = Gene
fields = ['gene_id', 'entity', 'start',
'stop', 'sense', 'start_codon',
'sequencing', 'ec']
A model form instance has a save
method that validates and saves a record for the data in the form, either a new record or updating an existing one if we use instance
(see the docs).
So the view function can just become:
def create_gene(request):
if request.method == 'POST':
form = GeneForm(request.POST)
if form.is_valid():
gene = form.save()
return HttpResponseRedirect('/create_gene/')
else:
form = GeneForm()
return render(request, 'genedata/create_gene.html', {'form': form})
3.406 Validators
By default the is_valid
method we’ve been calling on the forms checks the data types match.
If we want to do more than that we can write our own validation functions.
To do that we override the clean
method on the class. First call the parent clean (the basic one) then do custom stuff:
def clean(self):
cleaned_data = super(GeneForm, self).clean()
# my validation logic here
Validation errors are shown, see the docs
3.5 Django Generic Views
So far we’ve been using custom views to have fine-grained control on what’s happening with each request.
But if our application is very generic CRUD, there are some convenience class-based views that Django provides.
These are contained in the django.views.generic
package. Examples include ListView
and DetailView
for displaying lists or a single item.
These just make the code terser, so we can say something like this in our views.py:
from django.views.generic import ListView
class GeneList(ListView):
model = Gene
#this tells us the context object in the templates
context_object_name = 'master_genes'
template_name= 'genedata/index.html'
Then in our urls.py we just do:
urlpatterns = [
path('', views.GeneList.as_view(), name='index'),
]
3.503: By default the class view will only get the single context object specified. To override that you need to override the get_context_data
method. This lecture shows you how. Use a mixin if you are doing it everywhere.
3.504: Shows how the CreateView
class can be used to shorten the code for a typical create scenario. You need to add class fields for the model, template_name, form_class, and success_url.
3.505: Shows how the DeleteView
class works. It requires a confirmation template blob.
3.506: Shows how the UpdateView
class works. We specify which fields can be updated.
3.507: Refactors some older functions, shows more involved logic on context objects and template selection within a class view.
3.508: Continues 3.507, with the second function.
In this way of working we configure the application rather than define our own logic. The logic is mainly under the hood, with some custom logic applied through overrides.