Django - robbiehume/CS-Notes GitHub Wiki
- Good tutorial (Real Python)
- Django docs tutorial
- W3 schools tutorial
- External documentation sites:
- Uses a design similar to MVC, called MVT (model, view, template)
- The view and template in Django’s MVT pattern make up the view in the MVC pattern of other web frameworks
- Although it’s based on the MVC pattern, Django handles the controller part itself
- There’s no need to define how the database and views interact, it’s all done for you
- Benefits of Django: rapid development, provides extensive library (security, sessions, authentication, etc.), it's very scalable
-
Django Rest Framework (DRF) allows for REST APIs:
- It has things such as serializers, views, routers, authentication, etc. to make writing code easier and cleaner
- A Django website consists of a single project that's split into separate apps
- The idea is that each app handles a self-contained task that the site needs to preform
- A Django project contains at least one app. But even when there are more apps in the Django project, you commonly refer to a Django project as a web app
- A Django site starts off as a project, and you build it up with a number of applications that each handle separate functionality. Each app follows the model-view-template pattern
-
Project:
- The Django project holds some configurations that apply to the project as a whole, such as project settings, URLs, shared templates and static files
-
App:
- Each app can have its own database, and it’ll have its own functions to control how it displays data to the user in HTML templates
- Each app also has its own URLs as well as its own HTML templates, and static files, such as JavaScript and CSS
- Django apps are "pluggable": You can use an app in multiple projects, and you can distribute apps, because they don’t have to be tied to a given Django installation.
-
django-admin start-project <project_name>
: creates a new project -
__init__.py
: tells Python to treat the directory -
asgi.py
/wsgi.py
: they allow django to communicate with the web server; you don't have to deal with them yourself -
settings.py
: overall settings file -
urls.py
: used to configure URL routes- This can be at the project- or app-level
-
manage.py
: a special command line tool to do different things -
python manage.py startapp <app_name>
(run from within project): create a new app- Then go into the project subdirectory (<proj_name>/<proj_name>) and add the app_name to
settings.py
INSTALLED_APPS
list
- Then go into the project subdirectory (<proj_name>/<proj_name>) and add the app_name to
-
python manage.py runserver <optional_port_num>
: starts the project server
- Remove all references / imports / foreign keys to the app
- Remove models from
models.py
- Run
makemigrations
to generate table-deleting migration - Run tests locally and verify correctness
- (Optional) Squash references in other apps' migrations if needed
- Apply migrations using
python manage.py migrate
- Remove the app from
INSTALLED_APPS
- Delete the app folder from the project
-
Apps: At the heart of every Django website is apps, which are small pieces of the project that make up the entire website
- Apps typically contain some kind of database models, URLs, views, and other info about about a particular part of the website
- Views: functions that are in charge of processing a users request when they visit a certain URL or endpoint on the website.
-
URL Routing: to handle URL routing in a Django app, you create URL patterns in a list and attach different paths to those views
- This is how Django knows which view to fire off when a user visits a URL on the website
- This can be at the project or app level
- project-level (
my_project/my_project/urls.py
)from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('my_app.urls')) # this will forward all URLs that start with / to the my_app urls.py and be routed from there ]
- app-level (
my_project/my_app/urls.py
)from django.urls import path from . import views # gets all the local views urlpatterns = [ path('', views.hello_world, name='hello'), # this will map / to the views.hello_world() function path('todos/', views.todos, name='Todos') ]
- Path variables
-
path('<int:id>, views.index, name='index')
: looks for an integer in the path and passes it toindex()
- Other types:
-
<str:name>
(name is just the variable name)
-
-
-
from django.shortcuts import render, HttpResponse from .models import TodoItem def hello_world(request): return HttpResponse('hello world') # or you can render a template, e.g. return render(request, 'home.html') def todos(request): items = TodoItem.objects.all() return render(request, 'todos.html', {'todos': items})
- Different modules:
- HttpResponse
- For app templates, they need to be under app_name/templates/app_name
- Can use blocks to extend templates and fill in variables
-
home.html
(extends abase.html
template that already exists: link){% extends 'base.html' %} {% block title %} Home Page {% endblock%} {% block content %} <p>this is the home page</p> {% endblock %}
-
- Need to register models in
admin.py
- Any time you make a change to your database models, you need to make a "migration"
-
python manage.py makemigrations
- Can also specify an app to only migrate:
python manage.py makemigrations my_app
- Can also specify an app to only migrate:
python manage.py migrate
- The migration is some automated code that Django will apply to the database, which allows you to change your models and update them, while maintaining that data and not breaking any previous schema or data
- It's like git commit history
-
See exact SQL equivalent of a migration:
python manage.py sqlmigrate my_app <migration_number>
- Example:
python manage.py sqlmigrate my_app 0002
- Example:
-
Revert back to a specific migration:
- Example:
python manage.py migrate my_app 0002
-
NOTE: this only affects the database, not the migration files themselves
- If you also want to delete migration files, delete them manually and run
python manage.py makemigrations
- If you also want to delete migration files, delete them manually and run
- Example:
-
-
from django.db import models class TodoItem(models.Model): title = models.CharField(max_length=200) completed = models.BooleanField(default=False) def __str__(self): # method for printing TodoItem return self.text
-
from django.contrib import admin from .models import TodoItem # or my_app.models if the model is within the my_app app admin.site.register(TodoItem)
-
from django import forms class CreateNewList(forms.Form): name = forms.CharField(label='Name', max_length=200) check = forms.BooleanField()
- Types of views (DRF and built-in Django)
Scenario Recommended View Abstraction Level Super fast one-off JSON endpoint @api_view()
1️⃣ Function-based Need full control over HTTP methods APIView
2️⃣ Manual CBV Want less boilerplate but still control GenericAPIView + Mixins
3️⃣ Semi-automated CBV Need single-action CRUD views <Action>APIView
(e.g.,ListAPIView
)4️⃣ Specialized CBV Clean, standard full CRUD API ModelViewSet
(orViewSet
)5️⃣ Fully abstracted Serving HTML web pages Django ListView
,DetailView
, etc.Separate stack (Django CBV)
- A Model in Django is a python class that generally maps to a single database table
- SQLite comes with Django and is the default database
- To change to another DB, update
DATABASES
insettings.py
- To change to another DB, update
- To create a Model, we use a class structure inside of the relevant application’s
models.py
file - This class object will be a subclass of Django’s built-in class:
django.db.models.Model
- Django automatically handles the id field and uses it as the primary key
- But if you want, you can set a different primary key:
- E.g. for a product, set it as a SKU number:
sku = models.CharField(max_length=10, primary_key=True)
- E.g. for a product, set it as a SKU number:
- The model fields have specific types such as:
CharField
BooleanField
DateField
- For a complete reference, refer to the Django documentation
- Each field can also have constraints or options. For example:
- A
CharField
can have amax_length
constraint, indicating the maximum numbers of characters allowed. - Any field can have a
unique
option specifying that only unique instances of that field are allowed in the table.
- A
- Example of a blog post Model class:
-
from django.db import models class Post(models.Model): author = models.ForeignKey(User, on_delete=models.CASCADE) title = models.CharField(max_length=200) text = models.TextField() created_date = models.DateTimeField(default=timezone.now) published_date = models.DateTimeField(blank=True, null=True)
-
- Field types:
-
Strings:
-
CharField
: this is how we define text with a limited number of characters. -
TextField
: this is for long text without a limit
-
-
Numbers:
IntegerField
DecimalField
-
DateField
/DateTimeField
: for dates with and without times- Specific options:
-
auto_now
: -
auto_now_add
:
-
- Specific options:
-
ForeignKey
: this is a link to another model
-
Strings:
- Field validation options:
-
null
: if True, Django will store empty values as NULL in the database. Default is False -
blank
: if True, the field is allowed to be blank. Default is False -
max_length
: -
db_column
: the name of the database column to use for the field it is applied to. If this isn’t given, Django will use the field’s name -
default
: the default value for the field. This can be a value or a callable object. If callable, it will be called every time a new object is created -
help_text
: extra “help” text to be displayed with the form widget. This is useful for documentation even if our field isn’t used on a form -
primary_key
: if True, this field is the primary key for the model
-
- You can define relationships in either class, as Django will automatically create the reverse relationship
- It will use the default names below, or you can pass in
related_name=<attr_name_for_related_model>
- If you do use
related_name
s, it's best to do it consistently across your project or it will get ugly / hard to maintain
- If you do use
- It will use the default names below, or you can pass in
-
One-to-one:
customer = models.OneToOneField(Customer, on_delete=models.CASCADE, primary_key=True)
-
One-to-many:
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
- Use
PROTECT
if you don't want to delete the object if the customer is deleted
- Use
-
Many-to-many:
customer = models.ManyToManyField(Customer)
-
Relationship Forward Reverse Reverse Name (default) ForeignKey
Many-to-One One-to-Many <related_model>_set
ManyToManyField
Many-to-Many Many-to-Many <related_model>_set
OneToOneField
One-to-One One-to-One <related_model>
(no_set
)
- A circular dependency occurs when two classes depend on each other at the same time
- To handle this, you can set
related_name
to another name that doesn't clash- Or if you don't care about the reverse relationship, you can set
related_name='+'
- Or if you don't care about the reverse relationship, you can set
- When possible, we want relationships to be app agnostic (loosely coupled) and be able to be used in multiple places without having to customize it
- What we need is a generic way to define an object: type and ID
- This allows us to find the table and then use the ID to find the record (object) within the table (class)
-
To do this, we need 3 things:
- A special abstract model class called
ContentType
when defining the foreign keycontent_type = models.ForeignKey(ContentType)
- An object ID
object_id = models.PositiveIntegerField()
- The content object
content_object = GenericForeignKey()
- A special abstract model class called
- For an example, see
models.py
in the Mosh tutorial tags app - Use
GenericRelation
on target models:comments = GenericRelation(Comment)
- This adds a reverse accessor:
post.comments.all()
- These 4 cover 90%+ of real-world cases you'll encounter when working with Django models
-
models.CASCADE
: Deletes the related objects too (cascades deletion) -
models.PROTECT
: Prevents deletion by raising aProtectedError
-
models.SET_NULL
: Sets the foreign key field toNULL
(requiresnull=True
) -
models.RESTRICT
: LikePROTECT
, but less strict and only raises an error if there are dependent objects
-
TodoList
is just an example class for the below code snippets
-
t = TodoList(name='List 1') t.save()
- Or to create with saving automatically:
t = TodoList.objects.create(name='List 1')
- Still need to save when updating the object:
-
t.name = 'New name' t.save()
-
- Get all objects:
TodoList.objects.all()
- Get objects by attribute:
TodoList.objects.get(<attr>=<attr_val>)
- Get by id:
TodoList.objects.get(id=1)
- Get by name (if it has that attribute):
TodoList.objects.get(name='List 1')
- Get by id:
- Filter:
TodoList.objects.filter(<attr_functions>)
TodoList.objects.filter(name__startswith="Robbie")
- Delete:
TodoList.objects.get(id=1).delete()
- If a class has a foreign key of another object, then you can create that class through the foreign key class
- Ex:
-
class Item(models.Model): todolist = models.ForeignKey(TodoList, on_delete=models.CASCADE) text = models.CharField(max_length=300)
- Create
Item
forTodoList
t
:t.item_set.create(text='groceries')
- Query
Item
s: t.item_set.all()- NOTE:
item_set
is because the class name is Item; it follows the structure of<lowercase_class_name>_set
- NOTE:
-
- Routing can be two types static or dynamic
- Static: a specific URL that leads to a specific page
- Ex:
/home
--> home page,/about
--> about page
- Ex:
- Dynamic: URLs that lead to the same page, but depending on the variable part of the URL, the content shown can be different
- Ex:
/profile/101011
and/profile/102045
lead to the same endpoint but the page is rendered according to the profile id
- Ex:
- In static routing, we specify a constant URL string as a path in the
urls.py
file - NOTE: the path is a case-sensitive string. Therefore,
/home
and/Home
are two different URLs
- In dynamic routing, we don’t specify a constant URL path. Instead, we pass a URL with variable parameters in it
- The parameters may already be present in the URL itself, or they may be the result of user input
- Syntax of dynamic routes
- To add a variable in a path, we use it between the angle brackets
- For example,
<question_id>
, wherequestion_id
is the name of the variable we pass to thepath
as the first parameter
- For the dynamic routes mentioned above, we can also convert the variable to another data type:
-
str
: Matches any non-empty string, excluding the path separator,'/'
-
int
: Matches zero or any positive integer -
slug
: Matches any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters- For example,
building-your-1st-django-site
- For example,
-
uuid
: Matches a formatted UUID -
path
: Matches any non-empty string, including the path separator,'/'
- This allows us to match against a complete URL path rather than a segment of a URL path, as with
str
- This allows us to match against a complete URL path rather than a segment of a URL path, as with
-
- The
include()
function allows us to look for a match with the path defined in theurls.py
file of our project, and link it back to our application’s ownurls.py
file - Whenever Django encounters
include()
, it takes whatever part of the URL matched up to that point and sends the remaining string to theurls.py
file of the specified application- Ex:
python manage.py createsuperuser
- Click above link to see Python templating and differences between Django Template Language (DTL) and Jinja2
- The syntax of the Django template language involves four constructs:
- Variables: get replaced with values when the template is evaluated and rendered
-
Tags: used to control the logic of the template
- These can be conditions, loops, or filters
- Filters: used to modify variables for display
- Comments: used to comment-out part of a line in a template
- Syntax:
{{ variable }}
- Use: variables are used inside templates by passing any Python object to the
render()
functionreturn render(request,"index.html", context = any_object)
- In the example above,
index.html
is the name of the template andcontext
is the name of the object we are passing- Context can be a a JSON object, class, etc.
-
NOTE: tags must always have an end tag
- Ex:
{% endfor %}
,{% endif %}
,{% endfor %}
- Ex:
-
for
tag: used to loop over each item in an iterable-
<ul> {% for element in name_of_list %} <li>{{ element }}</li> {% endfor %} </ul>
-
-
if
,elif
, andelse
tags:-
{% if condition %} .... {% elif condition %} .... {% else %} .... {% endif %}
-
-
Filters:
-
Syntax:
{{ variable | filter_name }}
- A
|
(called a pipe) is used to apply a filter- Basically, the syntax above will display the value of the
{{variable}}
after being filtered through thefilter_name
filter
- Basically, the syntax above will display the value of the
- Some filters can also take arguments. A filter argument looks like this:
{{ bio|truncatewords:30 }}
- This will display the first 30 words of the
bio
variable
- This will display the first 30 words of the
- Django provides about sixty built-in template filters
-
Below are some of the more commonly used template filters:
-
default
filter:{{ value | default:"Value not provided" }}
- Ex: for above, if
value
isn't provided, then it displays 'Value not provided.'
-
length
filter: filters the length of the variable. Works for both strings and lists{{ value | length }}
- Ex: if
value
is ['north', 'south', 'east', 'west'], the output will be 4
-
lower
filter:{{ value | lower }}
- Ex: if
value
is 'HeLlO tHeRe', then the output will be: 'hello there'
-
-
Syntax:
-
Comments:
-
Syntax:
{# #}
- Ex:
{# My first comment #} Educative
- A comment can also contain any template code, invalid or not
{# {% if foo %} bar {% else %} #}
- This syntax can only be used for single-line comments (no newlines are permitted between the
{# and #}
delimiters) - If you need to comment-out a multi-line portion of the template, see the comment tag
-
Syntax:
- Must add
'rest_framework'
to theINSTALLED_APPS
insettings.py
- Serializers allow us to convert complex Django data structures such as
QuerySet
or model instances into Python native objects that can be easily converted to JSON or XML format- A serializer also serializes JSON or XML to native Python
- The DRF provides a
serializers
package we can use to write serializers and validations when API calls are made to an endpoint using this serializer - To use serializers package, import it in serializers.py:
from rest_framework import serializers
- Example
serializers.py
:-
from rest_framework import serializers from my_app.user.models import User # User class from my_app/user/models.py class UserSerializer(serializers.ModelSerializer): # Rewriting some fields like the public id to be represented as the id of the object id = serializers.UUIDField(source='public_id', read_only=True, format='hex') created = serializers.DateTimeField(read_only=True) updated = serializers.DateTimeField(read_only=True) class Meta: model = User # List of all the fields that can be included in a request or a response fields = ['id', 'username', 'first_name', 'last_name', 'bio', 'avatar', 'email', 'is_active', 'created', 'updated'] read_only_field = ['is_active'] # List of all the fields that can only be read by the user
-
- Django at its core is based on the Model-View-Template (MVT) architecture
- The model communicates with the views (or controllers) and the template displays responses or redirects requests to the views
- When Django is coupled with DRF, the model can be directly connected to the view
- However, it's good practice to use a serializer between a model and a viewset