Contents

Django Basics: Microsoft Learn

Django lessons:

Basics

Start a new project

In venv cli, use:

django-admin startproject helloproject .
python manage.py runserver
python manage.py startapp hello_world

Then, in project settings.py add a config for the app under INSTALLED_APPS: ‘hello_world.apps.HelloWorldConfig’,

view of an app found in views.py (the ‘index’ function returns HttpResponse(“hello world”))

for the app, create a urls.py and registre the view url:

1
2
3
4
5
6
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'), # routes to the function 'index' in the module 'views'
]

Note: views.index is imported from the app, index is a function

Now link this back to the urls.py of the project. Use:

1
2
from django.urls import include, path
path('', include('hello_world.urls')),

Django ORM

(Django ORM) Although it’s possible to create applications that interact with a relational database directly, this direct interaction can lead to code that’s duplicated and not secure. This problem led to the introduction of object-relational mappers (ORMs), which separate database calls from objects. converting the results of queries from a database into data that the application can use requires extra code.

The ORM then:

  • Manages creating and updating the database as needed.
  • Handles the queries.
  • Converts (or maps) the requests that you make through your objects into the appropriate database calls.

Field types (model variables basically):

Fields define the data structure of a model. Fields might include the name of an item, a creation date, a price, or any other piece of data that the model needs to store. Different pieces of data have different data types, validation rules, and other forms of metadata. The Django ORM contains a rich suite of options to configure the fields of your models to your specifications. Field types map both to a database type and an HTML form control type (such as a text box or a check box).

Migrations / managing the database

The Django ORM goes beyond allowing you to interact with data. You can also use it to create and update the database itself through a process known as migrations. A migration is a collection of updates to be performed on a database’s schema. A database schema is the definition of the database itself, including all tables and columns, and relationships between those tables.

When we created our models and defined the fields, we also defined the tables, columns, and relationships between those tables. We created our schema definition by creating our models!

Migrations are used to create and update the database as our models change. As you likely know, software is constantly changing. How we define our models today might be different from how we define them tomorrow. Migrations abstract the process of updating the database away from us. We can then make changes to our models and use Django to perform the necessary changes to the database.

to make a migration (automatically created code to update the db):

1
python manage.py makemigrations

or per app:

1
python manage.py makemigrations dog_shelters 

To build the appropriate SQL statements, you can use sqlmigrate.

1
python manage.py sqlmigrate <app_label> <migration_name>

Show all migrations:

1
python manage.py showmigrations

To actually do the migrate:

1
python manage.py migrate <app_label> <migration_name>

The app_label and migration_name parts are optional. If you don’t provide either, all migrations will run. You’ll use this command often during development.

Create new db entries

Open a python shell using:

1
python manage.py shell   	(close with exit() again)

then:

1
2
3
from dog_shelters.models import Shelter, Dog
shelter = Shelter(name="Demo shelter", location="Seattle, WA")
shelter.save()  		# saves to the db using django.models functionality, equivalent to INSERT

You could update the location:

1
2
shelter.location = "Redmond, WA"
shelter.save()	

Can of course also oneline the save + assign new dogs to the shelter object (django auto adds the link in the db), e.g.:

1
2
Dog(name="Sammy", description="Cute black and white dog", shelter=shelter).save()
Dog(name="Roscoe", description="Lab mix", shelter=shelter).save()

Retrieve an entry

shelter.dog_set.all() -> gives all dogs in shelter Dog.objects.get(pk=1) -> gives first dog Dog.objects.filter(shelter__name=‘Demo shelter’) gives all dogs that are in shelter

Note: In case we’re making two calls to shelter.dog_set.all, Two calls would normally be a performance hit. Django has built-in caching, which will ensure that only one call to the database is made.

Admin site and users

python manage.py createsuperuser (wachtwoord nu was Testing!) *JUR3EXGm_@YMx_

models need to be registred as editable by the admin. Under project_name/admin.py add: from .models import Shelter, Dog admin.site.register(Shelter) admin.site.register(Dog)

names displayed by the admin site is whatever str returns.

Views

In programming, a view is typically a component that displays information to the user. The tasks that a view performs can vary depending on the framework and conventions, including responsibility for loading data.

In Django, a view is typically responsible for:

Validating a user’s request. Loading or modifying the appropriate data. Returning an HTML template with the information to the user.

retrieve a data view or 404

to retrieve a specific object, use functions: get_object_or_404 and get_list_or_404

template rendering/linking

example:

def shelter_list(request): shelters = Shelter.objects.all() # get the data list context = { ‘shelters’: shelters } # return render(request, ‘shelter_list.html’, context) # ‘shelter_list.html’ is typically found in the templates folder! It puts the data(view) into action.

path url parameters

example: path(‘shelter/int:pk’, views.shelter_detail, name=‘shelter_detail’)

The associated view function would have the following signature: def shelter_detail(request, pk): # code

So:

1
2
3
4
5
def shelter_detail(request, pk):
''' load a specific shelter by its pk or primary key, create the context object for the template, and then render the template for the user '''
    shelter = get_object_or_404(models.Shelter, pk=pk)
    context = {'shelter': shelter}
    return render(request, 'shelter_detail.html', context)

Now, dont forget to add the path to urlpatterns in a urls.py of the app, and also link those urls to the url.py of the project. (this often is just on the same level (’’), but it separates the urls nicely and containerized.

templates

Syntax

variables

variables are displayed/called in the template using {{ object.name }} .

filters/pipelines

You can filter or pipe the variable using: {{ object.name | capfirst }}

Tags/ python functions

calling on programming functions is done using {{% funcitoncall %}}. Functions often look like python but are different (this is very similar to hugo). So:

1
2
3
4
5
{% if dogs %}
    <h2>There are {{ dogs | length }} ready for adoption!</h2>
{% else %}
    <h2>We have no dogs ready for adoption. Please check back later!</h2>
{% endif %}

Other example with for loop:

1
2
3
4
5
<ul>
    {% for dog in dogs %}
        <li>{{ dog.name }}</li>
    {% endfor %}
<ul>

A tag that gives the urlpattern corresponding with the shelter_detail name, linking to a view. The shelter_detail view function requires an argument pk, which is given by the shelter.id (in this case shelter is an object defined by a for loop). Because we use url, we can just change the urlconfig (urls.py) file instead of hardcoding any urls. {% url ‘shelter_detail’ shelter.id %}

Template inheritance

Use {% block placeholder %} to call on placeholder to provide the content. Example of a PARENT page:

{% block title %}Shelter site{% endblock %}

Shelter site

{% block content %} {% endblock %}

This inserts the content and a standard title. The title text ‘Shelter site’ can be overriden by a child later!

child page

We can create a child page from the parent by using the ’extends’ keyword. With this keyword, we provide the name of the HTML file of the parent template. The parent is the previous example. The blocks now fill in the actual content of the named blocks used in the parent! (I guess this behaviour is triggered by using the extends function).

{% extends “parent.html” %}

{% block title %} Welcome to the Shelter site! {% endblock %}

{% block content %} Thank you for visiting our site! {% endblock %}

Generic views

In the previous examples, for loading a individual item (detail view), we used the following workflow:

  • Load the item from the database by ID.
  • If the item isn’t found, return a 404.
  • If the item is found, pass the item to a template for display.

Fact is, this will always be the workflow. Generic views accomodate this, we dont have to code this ourselves! Corresponding to what weve written before, Django includes two generic views for displaying data: DetailView and ListView

In these, we can:

  • Set class variable template_name to the name of the template to be used. The default is _detail.html. (!! This default is not explicitly set. So for model Dog, the template name to use becomes dog_detail.html)
  • Set class variable context_object_name to the name of the variable we want to use in our template (So like title, or content as in the examples before).

While creating the corresponding path to link the view, use the as_view() method to convert the class into a view. Example: path(‘dog/int:pk’, views.DogDetailView.as_view(), name=‘dog_detail’)

DetailView vs ListView

While using a ListView, the primary difference with DetailView is that ListView is designed to work with any form of a query that returns multiple items. As a result, you must override the get_queryset function. The functionget_queryset is called by the generic view system to retrieve the items from the database, which allows you to order or filter your items as needed.

Generic Views for data editing

Like code for displaying data, we can also create views for editing data. This workflow is much more intense to program yourself so here generic views become of a larger importance.

Data Creation workflow

At the surface, the code to allow a user to create an item might seem trivial. As it turns out, it’s a deceptively involved process.

  • The user sends a GET request to signal they want the form to create a new item.
  • The server sends the form with a special token to prevent cross-site request forgery (CSRF).
  • The user completes the form and selects submit, which sends a POST request to indicate the form has been completed.
  • The server validates the CSRF token to ensure no tampering has taken place.
  • The server validates all information to ensure it meets the rules. An error message is returned if validation fails.
  • The server attempts to save the item to the database. If it fails, an error message is returned to the user.
  • After successfully saving the new item, the server redirects the user to a success page. This process requires quite a bit code! Most of it’s boilerplate, which means it’s the same every time you create it.

Forms

Forms are needed in the data creation workflow. Instead of coding this ourselves too, we notice that our django models already indicate everything we need, like data types. These are coupled to various html elements by default, like a boolean to a checkbox.

Generic Views for data editing: CreateView, UpdateView, and DeleteView

The classes used for creating, updating, and deleting data are called CreateView, UpdateView, and DeleteView.

CreateView

The class CreateView is used to allow a user to create items. It walks through the preceding process and dynamically creates the form. After success, it displays the detail page for the newly created item.

You specify the model and template_name you want to associate with it just as you would with the other generic views. The key difference for CreateView is the inclusion of a fields property where you list the editable fields. By using this property, you can ensure fields that shouldn’t be edited, like a creation date, don’t appear on the form.

Example:

1
2
3
4
class DogCreateView(generic.CreateView):
    model = models.Dog
    template_name = 'dog_form.html'
    fields = ['name', 'description', 'shelter']

UpdateView

The class UpdateView behaves in an identical fashion to CreateView. The only difference is that it automatically loads an item based on the pk parameter. Django uses this convention for the primary key for an item.

After successfully creating or updating an item, Django redirects to the details page for the item. It retrieves the URL for the details by using get_absolute_url on the associated model. You implement this method by returning the correct URL. You can retrieve the appropriate URL from URLconf by using reverse. Note kwargs is used to pass the pk or primary key parameter to the route.

We adapt the model we have to accommodate this redirect, e.g.:

1
2
3
4
5
6
7
from django.db import models
from django.urls import reverse

class Dog(models.Model):
    # Existing code
    def get_absolute_url(self):
        return reverse('dog_detail', kwargs={"pk": self.pk})

DeleteView

In deleting a view, we dont need the ‘fields’ attribute because we delete the complete item (so all fields either way). Also, the redirect is now not existing anymore. We can set the attribute success_url, either hardcoded or found lazily (e.g. first you encounter, only on request) using reverse_lazy:

1
2
3
4
5
6
7
from . import models
from django.views import generic
from django.urls import reverse_lazy

class AuthorDelete(DeleteView):
    model = Author
    success_url = reverse_lazy('author-list')

Note: We use reverse_lazy because of the order in which information is loaded into Django.

Form templates for create and update

The generic views can create the HTML form for us dynamically. All we need to provide is a template to act as the placeholder for the form. The placeholder template ensures the form matches the rest of our site. Fortunately, we don’t need much code to create it.

The generic views automatically create a form variable for our template to use. The form elements provided by Django can be displayed inside <p> tags or as a <table>.

The form variable contains all of the appropriate HTML to create the controls from the form. It doesn’t contain the <form> tag itself or a submit button. Our template must include four items:

  • The form element with the method set to POST because this setting triggers the save operation on the server.
  • The code {% csrf_token %} to add the CSRF token to prevent spoofing.
  • The code form.as_p or form.as_table to display the dynamically generated form.
  • The submit button.

The following code can act as the host for any generic view form.

1
2
3
4
<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save</button>
</form>

Django crispy forms

The standard forms are ugly and not editable in style. Therefore we use the app django-crispy-forms. Install with:

pip install django-crispy-forms
pip install crispy-bootstrap5

The last import is needed to make bootstrap actually work, for some reason.

Add to django under the INSTALLED_APPS as:

'crispy_forms',
"crispy_bootstrap5",

Then as a new settings variable, to define which css package we will use, add: CRISPY_TEMPLATE_PACK = ‘bootstrap5’

Ideas to add next

  • Marker Customization: how does the marker look, what info is displayed
  • Relational Filtering: filter what markers are displayed, filter what data is shown.
  • Clustering of frontend/backend markers/data
  • Geocoding services (focus the map on the location of the user and start loading the markers outwards from there)