How to Build a Web Application from Scratch Using Python and Django

Django is a full stack web framework, a collection of Python libraries that allows users to build a quality web application efficiently. Django provides every mean for backend such as “Django admin”, which is a built-in backend that let users to administer their web applications without letting them having to code much. In this article, we are going to implement a blog that gives your blog some functionalities, such as post. In other words, we will build the models like “Post”, which provide the basic operations such as, list, detail, edit, and delete.

If you need more help, you can always check out the following websites: The official source code repository, Django tutorial

Install Django and set up environments

Python installation

If you haven’t installed Django, you can simply install Django using Python package manager, “pip”.

$ shell> pip install “django>=1.10”

You should install Django 1.10 version or above.

Set up an environment for web development

python3 -m venv myenv
$ source myenv/bin/activate
$ pip install django==1.8

Create Your First Django Project

Let’s create a project at your current directory that you set up for the project by using these commands.

$ dev/firstProject> python manage.py migrate # apply models to DB
$ dev/firstProject> python manage.py createsuperuser # create superuser account
$ dev/firstProject> python manage.py runserver # activate development server
  • The Python command could be python, python3 or python3.5 – it depends on your python3 interpreter and machines.
  • It is recommended to use Chrome or Firefox as your web browser as your web browser in web development since they provide a quality development tools

Migrations

In Django, you can create a table that is mapped to its model at Database after the creation of Model class. Migration is the process of applying the modification or creation of Python Model class to DB. This is executed through the ORM (Object-Relational Mapping) service that is provided by Django.

There are two major steps, preparation for Migration, and applying it to DB, to create tables from Django Model class. Specifically, you can follow these steps:

  1. Add Django App you want to INSTALLED_APPS lists at settings.py
  2. Execute the command below to create or modify table schemas from Model class:
$ ./manage.py makemigrations

This creates a subfolder called “migrations” within Django App, and make python migration fils to create and modify tables.

  1. This command is used to apply migrations to DB.
$ ./manage.py migrate

Create the First app, “Blog”

The structure of the project we are creating is following:

firstProject
├───manage.py # a administrative tool for your web application
└───mysite
       settings.py  # a setting file for settings of your web application
       urls.py     # routing, a pattern list for `urlresolver`
       wsgi.py
       __init__.py

1.Add the Basic Settings

Create a file settings.py under mysite/

Set the time zone that you want by modifying TIME_ZONE at settings.py

TIME_ZONE = ‘Asia/Seoul’

In order to add the static file path, let’s add the following codes:

STATIC_URL = ‘/static/’
STATIC_ROOT = os.path.join(BASE_DIR, ‘static’)

Open “mysite/settings.py”, and add the app that we just created to INSTALLED_APPS

INSTALLED_APPS = (
   ‘django.contrib.admin’,
   ‘django.contrib.auth’,
   ‘django.contrib.contenttypes’,
   ‘django.contrib.sessions’,
   ‘django.contrib.messages’,
   ‘django.contrib.staticfiles’,
   ‘blog’,
)
  1. Set up a Database

By default, sqlite3 is installed, and

DATABASES = { ‘Default’: {‘ENGINE’: ‘django.db.backends.sqlite3’, ‘NAME’: os.path.join(BASE_DIR, ‘db.sqlite3’),}}

In order to create DB, Django uses manage.py

$ python manage.py migrate

Now, we can run the server to see whether it actually works

$ python manage.py runserver
  1. Create a Model

In Django, a model is a layer that provides data service. It is supposed to be defined at “models.py” that is created by default in each Django App. You can define more than one model class at “models.py” module, and each model class corresponds to each table at database.

class Post(models.Model):
   title = models.CharField(max_length=100)
   content = models.TextField()
   # tags = models.CharField(max_length=100, blank=True)
   # lnglat = models.CharField(max_length=50, blank=True, help_text=’test’)
   created_at = models.DateTimeField(auto_now_add=True)
   updated_at = models.DateTimeField(auto_now=True)
   def __str__(self):
return self.title
   def get_absolute_url(self):
return reverse(‘blog:post_detail’, args=[self.pk])

There are many field types in Django, and the following chart is a brief summary of primary field types.

Field Type Info
CharField A string field, for small- to large-sized strings.
IntegerField An integer. Values from -2147483648 to 2147483647 are safe in all databases supported by Django.
DecimalField A fixed-precision decimal number, represented in Python by a Decimal instance.
TextField A large text field.
DateTimeField A date and time, represented in Python by a datetime.datetime instance.
BooleanField A true/false field.
BinaryField A field to store raw binary data.
FileField A file-upload field.
ImageField Inherits all attributes and methods from FileField, but also validates that the uploaded object is a valid image.
UUIDField A field for storing universally unique identifiers.

If you want to understand the model types in the above, You can refer model field type reference here.

Model fields can have many different options or arguments depending their field types. For example, CharField can have “max_length”, which is the option that means the max of character string. Generally, field options are assigned as arguments at constructors. The following chart is a summary of the options that can be applied in all field types.

Field Option Info
null (Field.null) If null=True, store an empty value to DB as NULL. In DB, Null is permitted.

e.g.) models.IntegerField(null=True)

blank (Field.blank) If blank=False, the field is Required field, and if blank is True, it’s Optional Field.

e.g.) models.DateTimeField(blank=True)

primary_key (Field.primary_key) It shows that the field is Primary Key.

e.g.)  models.CharField(max_length=10, primary_key=True)

unique (Field.unique) It shows that the field is unique at a table. It also creates Unique Index to its column.

e.g.) models.integerField(unique=True)

default (Field.default) It assigns a default value of a field.

e.g.) models.CharField(max_length=3, default=”AA”)

To use the model created, make migration files, and execute migrations to database.

$ python manage.py makemigrations blog
$ python manage.py migrate blog

Django Framework provides ForeignKey, ManyToManyField, and OneToOneField classes to express relationships between tables or fields. In particular, ForeignKey is commonly used to express relationships of models, tables, Many-To-One, and One-To-Many. If you want to get more information about this, please look up this link: https://docs.djangoproject.com/es/1.11/ref/models/fields/#module-django.db.models.fields.related

  1. Create Django Administrator

We need to add these lines at “blog/admin.py” to be accessed from administrative panel

from django.contrib import admin
from .models import Post
admin.site.registerfrom django.contrib import admin
from .models import PostPost)
$ python manage.py createsuperuser
$ python manage.py runserver

Create an id to log in the panel.

To test the above, try to add a post at Posts

  1. Upload the project to Git repository

Initialize a repository and commit it.   

If you need more help, see this tutorial:

https://git-scm.com/book/en/v2/Getting-Started-Getting-Help

  1. Add URL patterns

Django uses regular expressions for URL. Bring blog to mysite by following these codes

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
   url(r’^admin/’, include(admin.site.urls)),
   url(r”, include(‘blog.urls’)),
]

Add “blog/url.py

from django.conf.urls import url
from . import views

urlpatterns = [
   url(r’^$’, views.post_list, name=‘post_list’),
]
  1. Create Views

In Django, a view is like “Controller” of other MVC Framework. It is a place that holds a logic of an application. It gets some information from the model we created, and it pass the information to templates. Views are supposed to be defined at views.py of Django App, and each function defines each view. Each view, a callable object, takes HTTP as an input parameter, and returns HTTP Response.

Open “blog/views.py”, and create “post_list”.

from django.shortcuts import render

def post_list(request):
   return render(request, ‘blog/post_list.html’, {})
  1. Create HTML Templates

Whereas View in Django has the role of the Controller has the role of the Controller in other MVC Framework, Django’s Template works as View in other MVC Framework. Templates are used to build dynamic web pages applying data that is delivered from View to the templates. Django development guidelines recommends to follow this directory structure such as, “/home/templates/home/index.html”. The reason why the directory structure is encouraged is that there might be name collision in the process of importing templates. For example, if there is “layout.html” at App1, and App2 has a template that has the same file name, and if App2’s view calls “create.html”, it uses “create.html” of App1. This is because when it finds its templates, it doesn’t really find templates within its app, but it finds template folders in the entire App in order.

Create “post_list.html” under “blog/templates/blog”. Be careful with the directory structure.

  1. Add Django ORM and QuerySets

In Django, QuerySets(https://docs.djangoproject.com/en/2.0/ref/models/querysets/) is used to align or process data.

Access shell using this command at console.

$ python manage.py shell

After import models, we can use QuerySets.

>>> from blog.models import Post
>>> Post.objects.all()
[<Post: Hello World>, <Post: Koala>]
>>> from django.contrib.auth.models import User
>>> User.objects.all()
[<User: edward>]
>>> me = User.objects.get(username=’edward’)
>>> Post.objects.create(author=me, title=’Goodbye my friend’, text=’bye’)
<Post: Goodbye my friend>
>>> Post.objects.all()
[<Post: Hello World>, <Post: Koala>, <Post: Goodbye my friend>]

You can do filter QuerySets as below shows.

>>> Post.objects.filter(author=me)
[<Post: Hello World>, <Post: Koala>, <Post: Goodbye my friend>]
>>> Post.objects.filter(title__contains=’Koala’)
[<Post: Koala>]
>>> from django.utils import timezone
>>> Post.objects.filter(published_date__lte=timezone.now())
[]
>>> post = Post.objects.get(title__contains=”Goodbye”)
>>> post.publish()
>>> Post.objects.filter(published_date__lte=timezone.now())
[<Post: Goodbye my friend>]

It is possible to align and call it all at once by chaining.

>>> Post.objects.order_by(‘created_date’)
[<Post: Hello World>, <Post: Koala>, <Post: Goodbye my friend>]
>>> Post.objects.order_by(‘-created_date’)
[<Post: Goodbye my friend>, <Post: Koala>, <Post: Hello World>]
>>> Post.objects.filter(published_date__lte=timezone.now()).order_by(‘published_date’)
  1. Add Dynamic Data in Templates

Modify “blog/views.py”.

from django.shortcuts import render
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by(‘published_date’)
return render(request, ‘blog/post_list.html’, {‘post’: posts})

Modify “blog/templates/blog/post_list.html”.

<div>
   <h1><a href=“/”>First Blog</a></h1>
</div>

{% for post in posts %}
   <div>
       <p>published: {{ post.published_date }}</p>
       <h1><a href=“”>{{ post.title }}</a></h1>
       <p>{{ post.text|linebreaks }}</p>
   </div>
{% endfor %}
  1. Add CSS

Put static files to “blog/static”.

Write out “blog/static/css/blog.css”.

h1 a {
   color: #FCA205;
}

Apply it to templates

{% load staticfiles %}
<!doctype html>
<html lang=“ko”>
<head>
   <meta charset=“utf-8”>
   <title>First Blog </title>
   <link rel=“stylesheet” href=“{% static ‘css/blog.css’ %}”>
</head>
<body>
 <div>
     <h1><a href=“/”>First Blog</a></h1>
 </div>

 {% for post in posts %}
     <div>
         <p>published: {{ post.published_date }}</p>
         <h1><a href=“”>{{ post.title }}</a></h1>
         <p>{{ post.text|linebreaks }}</p>
     </div>
 {% endfor %}
</body>
</html>
  1. Template Extension

When you develop a website, you will notice that many pages have the common HTML  codes. It is very inefficient to use the repeated code at web pages. Django provides a functionality called, “Template Extension” or “Template Inheritance”, which is like Inheritance in Object Oriented Programming to solve the problem.

Write out “base.html” as follows.

{% load staticfiles %}
<!doctype html>
<html lang=“ko”>
<head>
   <meta charset=“utf-8”>
   <title>First Blog </title>
   <link rel=“stylesheet” href=“//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css”>
   <link rel=“stylesheet” href=“//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css”>
   <link rel=“stylesheet” href=“{% static ‘css/blog.css’ %}”>
</head>
<body>
   <div class=“page-header”>
       <h1><a href=“/”>First Blog</a></h1>
   </div>
   <div class=“content container”>
       <div class=“row”>
           <div class=“col-md-8”>
           {% block content %}
           {% endblock %}
           </div>
       </div>
   </div>
</body>
</html>

Now import the base file, and put the contents at the content block.

  1. Create a Page to Add Post Details

Now we are going to make a page to see blog posts.

Modify the link of “blog/templates/blog/post_list.html”.

<h1><a href=“{% url ‘post_detail’ pk=post.pk %}”>{{ post.title }}</a></h1>

Add “post_detail” at “blog/urls.py”, which means we are going to store the contents at “pk” variable. Pk means primary key.

from django.conf.urls import url
from . import views

urlpatterns = [  url(r’^$’, views.post_list, name=‘post_list’),  url(r’^post/(?P<pk>[0-9]+)/$’, views.post_detail, name=‘post_detail’),
]

Add post_detail at “blog/views.py”. If there is no key, 404 Not Found page is needed. In this case, the function, “get_object_or_404”, can be used.

from django.shortcuts import render, get_object_or_404

# …

def post_detail(request, pk):
   post = get_object_or_404(Post, pk=pk)
   return render(request, ‘blog/post_detail.html’, {‘post’: post})

Now create “blog/templates/blog/post_detail.html”.

{% extends ‘blog/base.html’ %}

{% block content %}
   <div class=“post”>
       {% if post.published_date %}
           <div class=“date”>
               {{ post.published_date }}
           </div>
       {% endif %}
       <h1>{{ post.title }}</h1>
       <p>{{ post.text|linebreaks }}</p>
   </div>
{% endblock %}
  1. Create a Form to View Posts

Django provide a functionality that can simply create forms.

Create “blog/forms.py

from django import forms
from .models import Post


class PostForm(forms.ModelForm):
   class Meta:
       model = Post
       fields = (‘title’, ‘text’,)

Add a link that can access to the form at “blog/templates/blog/base.html

<a href=“{% url ‘post_new’ %}” class=“top-menu”><span class=“glyphicon glyphicon-plus”></span></a>

Add this at “blog/url.py

url(r’^post/new/$’, views.post_new, name=‘post_new’),

Add form and a view, “post_new”, at “blog/views.py”.

from django.shortcuts import redirect
from .forms import PostForm

#…

def post_new(request):
   if request.method == “POST”:
       form = PostForm(request.POST)
       if form.is_valid():
           post = form.save(commit=False)
           post.author = request.user
           post.published_date = timezone.now()
           post.save()
           return redirect(‘blog.views.post_detail’, pk=post.pk)
   else:
       form = PostForm()
   return render(request, ‘blog/post_edit.html’, {‘form‘: form})
  1. Create a Page to Edit Posts

Add “blog/templates/blog/post_edit.html

{% extends ‘blog/base.html’ %}

{% block content %}
   <h1>New post</h1>
   <form method=“POST” class=“post-form”>{% csrf_token %}
       {{ form.as_p }}
       <button type=“submit” class=“save btn btn-default”>Save</button>
   </form>
{% endblock %}

Add this following link at “blog/templates/blog/post_detail.html

<a class=“btn btn-default” href=“{% url ‘post_edit’ pk=post.pk %}”><span class=“glyphicon glyphicon-pencil”></span></a>

Add the following code at “blog/urls.py

  url(r’^post/(?P<pk>[0-9]+)/edit/$’, views.post_edit, name=‘post_edit’),

Add a view, “post_edit” at “blog/views.py

def post_edit(request, pk):
   post = get_object_or_404(Post, pk=pk)
   if request.method == “POST”:
       form = PostForm(request.POST, instance=post)
       if form.is_valid():
           post = form.save(commit=False)
           post.author = request.user
           post.published_date = timezone.now()
           post.save()
           return redirect(‘blog.views.post_detail’, pk=pk)
   else:
       form = PostForm(instance=post)
   return render(request, ‘blog/post_edit.html’, {‘form‘: form})

Initialize the contents in advance by adding an instance at “PostForm()”.

What’s Next?

If you want to learn more about web development or need any further resources, you can try the following references.

The official tutorial of Django

Django Carrots Tutorial

Code Academy HTML&CSS Courses

Hello Web App: Learn How to Build a Web App

W3school.com

Django Tutorial: The Local library Website