Skip to content

Learn web development using Python and Django by building an Ordering App

License

Notifications You must be signed in to change notification settings

hanztura/django-tutorial--ordering-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Django Web Development with Python

Learn web development using Python and Django by building an Ordering App

Authors

  • Hanz Tura - Python and Node Backend Developer. For any help contact here.

Installation

  • python3.9 -m venv env
  • source env/bin/activate
  • pip install django==3.2
  • cd djangoorders
  • test if all went well by running python manage.py runserver and open http://localhost:8000
  • python manage.py migrate

Django Models (Database Modelling)

Customer

  • python manage.py startapp customers
  • define model Class 'Customer'
  • make sure to define the str instance method
  • register the customers app, insert at djangoorders.settings.INSTALLED_APPS
  • python manage.py makemigrations customers
  • python manage.py migrate
  • Create/Update a sample customer through management console python manage.py shell
from customers.models import Customer
c = Customer(first_name='Hanz', last_name='Tura')
c.first_name
c.last_name

c.pk  # returns none
c.save()
c.pk  # returns 1

c.street
c.country

c.street = '123 St.'
c.city = 'Cagayan de Oro'
c.state = 'Misamis Oriental'
c.zip_code = '9000'
c.save()  # to update record in database
c.street
c.country

c2 = Customer(first_name='John', last_name='Doe')
c2.save()
  • List/Get/Delete a record via management console:
from customers.models import Customer
queryset = Customer.objects.all()  # get the list of customers
queryset

c = Customer.objects.get(pk=1)  # get one record
c
c.delete()

Item

  • python manage.py startapp items
  • define model Class 'Item'
  • make sure to define the str instance method
  • register the items app, insert at djangoorders.settings.INSTALLED_APPS
  • python manage.py makemigrations items
  • python manage.py migrate
  • Create/List/Get sample items through management console python manage.py shell
from items.models import Item
obj = Item(name='Macbook Air', description='Apple M1 Chip with 8-Core CPU and 7-Core GPU 256 GB Storage', price='54990', code='202100001')
obj.save()

obj2 = Item(name='Printer', description='Epson Printer', price='2550', code='202100002')
obj2.save()

queryset = Item.objects.all()
queryset

Order

  • python manage.py startapp orders
  • define model Class 'Order'
  • make sure to define the str instance method
  • register the orders app, insert at djangoorders.settings.INSTALLED_APPS
  • python manage.py makemigrations orders
  • python manage.py migrate

Order Item

  • define model Class 'OrderItem' inside orders.models file
  • make sure to define the str instance method
  • python manage.py makemigrations orders
  • python manage.py migrate

Django Views

Function Views

  • Define a function view called customer_list inside the customers app's views.py file.
from django.shortcuts import HttpResponse

def customer_list(request):
    return HttpResponse('Customer List')
  • Connect the view into the Django URL configuration file djangoorders/urls.py
from django.contrib import admin
from django.urls import path
from customers import views as customer_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('customers/', customer_views.customer_list)
]
#customers/views.py/
from django.shortcuts import HttpResponse

from customers.models import Customer


def customer_list(request):
    customers = Customer.objects.all()
    return HttpResponse(customers)

Django Template

  • Render a simple html file using Django template.
  • Inside the customers app, create a folder called templates
  • Inside the templates folder, create another folder called customers
  • Inside the customers/templates/customers folder, create an html file called customer_list.html.
  • Edit the HTML file so it would contain:
<html>
  <head>
    <title>Customer List</title>
  </head>
  <body>
    <h1>Customers</h1>
    <ul>
      <li>John Doe</li>
      <li>Hanz Tura</li>
    </ul>
  </body>
</html>
  • Edit our customer_list view to render our html template:
from django.shortcuts import render
from django.shortcuts import HttpResponse

from customers.models import Customer


def customer_list(request):
    customers = Customer.objects.all()
    return render(request, 'customers/customer_list.html')
  • Let's improve the template and render the list of customers based on the records from our database:
<html>
  <head>
    <title>Customer List</title>
  </head>
  <body>
    <h1>Customers</h1>
    <ul>
      {% for customer in customers %}
      <li>{{ customer.last_name }}, {{ customer.first_name }}</li>
      {% endfor %}
    </ul>
  </body>
</html>
  • Pass the customers variable as a context in the customer_list view:
from django.shortcuts import render

from customers.models import Customer


def customer_list(request):
    customers = Customer.objects.all()
    context = {
        'customers': customers,
    }
    return render(request, 'customers/customer_list.html', context=context)
  • Let's try another way of displaying a list of records by using tables. Create templates/customers/customer_table.html:
<html>
  <head>
    <title>Customer List</title>
  </head>
  <body>
    <h1>Customers</h1>
    <section>
      <table>
        <thead>
          <tr>
            <th colspan="3"></th>
            <th colspan="5">Address</th>
          </tr>
          <tr>
            <th>ID</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Street</th>
            <th>City</th>
            <th>State</th>
            <th>ZIP Code</th>
            <th>Country</th>
          </tr>
        </thead>
        <tbody>
          {% for customer in customers %}
          <tr>
            <td>{{ customer.pk }}</td>
            <td>{{ customer.first_name }}</td>
            <td>{{ customer.last_name }}</td>
            <td>{{ customer.street }}</td>
            <td>{{ customer.city }}</td>
            <td>{{ customer.state }}</td>
            <td>{{ customer.zip_code }}</td>
            <td>{{ customer.country }}</td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </section>
  </body>
</html>
  • Add another view function named customer_table in the customers/views.py file:
from django.shortcuts import render

from customers.models import Customer


def customer_list(request):
    customers = Customer.objects.all()
    context = {
        'customers': customers,
    }
    return render(request, 'customers/customer_list.html', context=context)


def customer_table(request):
    customers = Customer.objects.all()
    context = {
        'customers': customers,
    }
    return render(request, 'customers/customer_table.html', context=context)
  • Connect the view in the URL configuration:
from django.contrib import admin
from django.urls import path

from customers import views as customer_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('customers/', customer_views.customer_list),
    path('customers/t', customer_views.customer_table),  # insert this line
]

Reusable Templates

  • Inside the project directory, create a root templates folder.
  • In the settings file djangoorders/settings.py, update the TEMPLATES.DIRS variable:
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR.joinpath('templates')  # insert this
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
  • In the root templates folder, create a template file base.html
<html>
  <head>
    <title>{% block title %}{% endblock title %}</title>
  </head>
  <body>
    <h1>{% block h1 %}{% endblock h1 %}</h1>
    <main>{% block main %}{% endblock main %}</main>
  </body>
</html>
  • Update the templates/customers/customer_table.html:
{% extends 'base.html' %} {% block title %}Customer List{% endblock title %} {%
block h1 %}Customers{% endblock h1 %} {% block main %}
<table>
  <thead>
    <tr>
      <th colspan="3"></th>
      <th colspan="5">Address</th>
    </tr>
    <tr>
      <th>ID</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Street</th>
      <th>City</th>
      <th>State</th>
      <th>ZIP Code</th>
      <th>Country</th>
    </tr>
  </thead>
  <tbody>
    {% for customer in customers %}
    <tr>
      <td>{{ customer.pk }}</td>
      <td>{{ customer.first_name }}</td>
      <td>{{ customer.last_name }}</td>
      <td>{{ customer.street }}</td>
      <td>{{ customer.city }}</td>
      <td>{{ customer.state }}</td>
      <td>{{ customer.zip_code }}</td>
      <td>{{ customer.country }}</td>
    </tr>
    {% endfor %}
  </tbody>
</table>
{% endblock main %}

URL Naming

  • Go to the djangoorders/urls.py and add a name paramater on each URL entry:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('customers/', customer_views.customer_list, name="customer_list"),
    path('customers/t', customer_views.customer_table, name="customer_table"),
    path('', customer_views.home, name="home"),
]
  • Inside the root templates folder, create a home.html file:
{% extends 'base.html' %} {% block title %}Ordering App{% endblock title %} {%
block h1 %}Ordering App - Home{% endblock h1 %} {% block main %}
<p>Welcome to Django Ordering App.</p>
<p>To start, click here to login.</p>
<ul>
  <li><a href="{% url 'customer_table' %}">Customers</a></li>
  <li>Items</li>
  <li>Orders</li>
</ul>
{% endblock main %}

App Views

Customer Views

Customer Create View
  • In the templates/customers/ create customer_create.html file:
{% extends 'base.html' %} {% block title %}New Customer{% endblock title %} {%
block h1 %}New Customer{% endblock h1 %} {% block main %}
<form method="POST">
  {% csrf_token %}
  <table>
    <tbody>
      <tr>
        <th><label>Last Name</label></th>
        <td><input type="text" name="last_name" /></td>
      </tr>
      <tr>
        <th><label>First Name</label></th>
        <td><input type="text" name="first_name" /></td>
      </tr>
      <tr>
        <th><label>Street</label></th>
        <td><input type="text" name="street" /></td>
      </tr>
      <tr>
        <th><label>City</label></th>
        <td><input type="text" name="city" /></td>
      </tr>
      <tr>
        <th><label>State</label></th>
        <td><input type="text" name="state" /></td>
      </tr>
      <tr>
        <th><label>ZIP</label></th>
        <td><input type="text" name="zip_code" /></td>
      </tr>
      <tr>
        <th><label>Country</label></th>
        <td>
          <select name="country">
            <option value="ph">Philippines</option>
            <option value="" disabled>Others</option>
          </select>
        </td>
      </tr>
      <tr>
        <th></th>
        <td>
          <button type="submit">Save</button>
        </td>
      </tr>
    </tbody>
  </table>
</form>
{% endblock main %}
  • Define another view function inside the customers/views.py:
# ... previous lines here

def customer_create(request):
    return render(request, 'customers/customer_create.html')
  • Connect the customer_create view in the URLs config:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('customers/new/', customer_views.customer_create, name="customer_create"),  # add this line
    path('customers/t/', customer_views.customer_table, name="customer_table"),
    path('customers/', customer_views.customer_list, name="customer_list"),
    path('', customer_views.home, name="home"),
]
def customer_create(request):
    data = {}
    if request.method == 'POST':
        data['first_name'] = request.POST.get('first_name')
        data['last_name'] = request.POST.get('last_name')
        data['street'] = request.POST.get('street', '')
        data['city'] = request.POST.get('city', '')
        data['state'] = request.POST.get('state', '')
        data['zip_code'] = request.POST.get('zip_code', '')
        data['country'] = request.POST.get('country', '')
        customer = Customer(**data)

        # make first and last name required
        if data['first_name'] and data['last_name']:
            try:
                customer.save()
                customer_list_url = reverse_lazy('customer_list')
                return HttpResponseRedirect(customer_list_url)
            except Exception as e:
                print(e)
        else:
            data['errors'] = 'First Name and Last Name fields are required'

    return render(request, 'customers/customer_create.html', context=data)
Customer Detail View
  • In the templates/customers/ create customer_detail.html file:
{% extends 'base.html' %} {% block title %} {{ customer|default:'' }} - Customer
Detail{% endblock title %} {% block h1 %}{{ customer|default:'' }} - Customer{%
endblock h1 %} {% block main %} {% if customer %}
<table>
  <tbody>
    <tr>
      <th>Last Name</th>
      <td>{{ customer.last_name }}</td>
    </tr>
    <tr>
      <th>First Name</th>
      <td>{{ customer.first_name }}</td>
    </tr>
    <tr>
      <th>Street</th>
      <td>{{ customer.street }}</td>
    </tr>
    <tr>
      <th>City</th>
      <td>{{ customer.city }}</td>
    </tr>
    <tr>
      <th>State</th>
      <td>{{ customer.state }}</td>
    </tr>
    <tr>
      <th>ZIP Code</th>
      <td>{{ customer.zip_code }}</td>
    </tr>
    <tr>
      <th>Country</th>
      <td>{{ customer.country }}</td>
    </tr>
  </tbody>
</table>
{% else %}
<p>Unable to find this customer.</p>
{% endif %} {% endblock main %}
  • Define another view function inside the customers/views.py:
# ... previous lines here

def customer_detail(request, pk):
    try:
        customer = Customer.objects.get(pk=pk)
    except Customer.DoesNotExist as e:
        print(e)
        customer = Customer.objects.none()

    context = {
        'customer': customer,
    }
    return render(request, 'customers/customer_detail.html', context=context)
  • Connect the customer_detail view in the URLs config:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('customers/new/', customer_views.customer_create, name="customer_create"),
    path('customers/t/', customer_views.customer_table, name="customer_table"),
    path('customers/<int:pk>/', customer_views.customer_detail,
         name="customer_detail"),
    path('customers/', customer_views.customer_list, name="customer_list"),
    path('', customer_views.home, name="home"),
]
Customer Update View
  • In the templates/customers/ create customer_update.html file:

{% block title %} {{ customer|default:'' }} - Edit Customer{% endblock title %}
{% block h1 %}{{ customer|default:'' }} - Edit Customer{% endblock h1 %}

{% block main %}
<form method="POST">
  {% csrf_token %}
  <table class="table">
    <tbody>

      {% if errors %}
        <tr>
          <td colspan="2"><span>{{ errors }}</span></td>
        </tr>
      {% endif %}

      <tr>
        <th><label>Last Name</label></th>
        <td><input type="text" name="last_name" value="{{ customer.last_name }}" /></td>
      </tr>
      <tr>
        <th><label>First Name</label></th>
        <td><input type="text" name="first_name" value="{{ customer.first_name }}" /></td>
      </tr>
      <tr>
        <th><label>Street</label></th>
        <td><input type="text" name="street" value="{{ customer.street }}" /></td>
      </tr>
      <tr>
        <th><label>City</label></th>
        <td><input type="text" name="city" value="{{ customer.city }}" /></td>
      </tr>
      <tr>
        <th><label>State</label></th>
        <td><input type="text" name="state" value="{{ customer.state }}" /></td>
      </tr>
      <tr>
        <th><label>ZIP</label></th>
        <td><input type="text" name="zip_code" value="{{ customer.zip_code }}" /></td>
      </tr>
      <tr>
        <th><label>Country</label></th>
        <td>
          <select name="country">
            <option selected value="ph">Philippines</option>
            <option value="" disabled>Others</option>
          </select>
        </td>
      </tr>
      <tr>
        <th></th>
        <td>
          <button type="submit">Save</button>
        </td>
      </tr>
    </tbody>
  </table>
</form>
{% endblock main %}
  • Define another view function inside the customers/views.py:
# ... previous lines here

def customer_update(request, pk):
    customer = Customer.objects.get(pk=pk)
    data = {'customer': customer}
    if request.method == 'POST':
        customer.first_name = request.POST.get(
            'first_name', customer.first_name)
        customer.last_name = request.POST.get('last_name', customer.last_name)
        customer.street = request.POST.get('street', customer.street)
        customer.city = request.POST.get('city', customer.city)
        customer.state = request.POST.get('state', customer.state)
        customer.zip_code = request.POST.get('zip_code', customer.zip_code)
        customer.country = request.POST.get('country', customer.country)

        if customer.first_name and customer.last_name:
            try:
                customer.save()
                customer_detail_url = reverse_lazy(
                    'customer_detail', kwargs={'pk': pk})
                return HttpResponseRedirect(customer_detail_url)
            except Exception as e:
                print(e)
                data['errors'] = str(e)
        else:
            data['errors'] = 'First Name and Last Name fields are required'

    return render(request, 'customers/customer_update.html', context=data)
  • Connect the customer_update view in the URLs config:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('customers/new/', customer_views.customer_create, name="customer_create"),
    path('customers/t/', customer_views.customer_table, name="customer_table"),
    path('customers/<int:pk>/', customer_views.customer_detail,
         name="customer_detail"),
    path('customers/<int:pk>/edit', customer_views.customer_update,
         name="customer_update"),  # add this line
    path('customers/', customer_views.customer_list, name="customer_list"),
    path('', customer_views.home, name="home"),
]
  • Let's make the look of our app a bit more interesting by using a CSS Framework.
Customer Delete View

Orders

Other Topics

CSS

  • Copy the root base.html and name it base_bulma.html add this line inside the head element:
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <title>{% block title %}{% endblock title %}</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css"
    />
  </head>
  <body>
    <div class="container">
      <h1 class="title is-1">{% block h1 %}{% endblock h1 %}</h1>
    </div>
    <main class="container">{% block main %}{% endblock main %}</main>
  </body>
</html>
  • In the templates/customers/ folder, create a new file base.html:
{% extends 'base_bulma.html' %}
  • Edit the first line of the following files (the line with the extends statement):

    • customer_create.html

    • customer_detail.html

    • customer_table.html

    • customer_update.html

<!-- {% extends 'base.html' %} FROM THIS-->
{% extends 'customers/base.html' %}
<!-- TO THIS -->
  • On every table elements, add class attribute with the value class="table"