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:
| |
Note: views.index is imported from the app, index is a function
Now link this back to the urls.py of the project. Use:
| |
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):
| |
or per app:
| |
To build the appropriate SQL statements, you can use sqlmigrate.
| |
Show all migrations:
| |
To actually do the migrate:
| |
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:
| |
then:
| |
You could update the location:
| |
Can of course also oneline the save + assign new dogs to the shelter object (django auto adds the link in the db), e.g.:
| |
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:
| |
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:
| |
Other example with for loop:
| |
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:
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:
| |
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.:
| |
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:
| |
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_porform.as_tableto display the dynamically generated form. - The submit button.
The following code can act as the host for any generic view 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)