Using raw Query to Fetch data from PostgreSQL in Django

Instead of using ORM, if you want to hard code a query in django, you can refer to below code

def getprices(request):
    print("hello backend")
    conn = psycopg2.connect(database="ecommpedia1", user="admin", password="admin", host="127.0.0.1", port="5432")
    cur = conn.cursor()   
    productid = "CAME747PNEUYMSHT"
    cur.execute("SELECT * FROM ecomm_time WHERE  flipkartproductid = '"+productid+"';") 
    mobile_records = cur.fetchall()
    print("mobile_records : ", mobile_records)
    print(type(mobile_records))
    return HttpResponse(mobile_records)

Creating GRID structure using bootstrap and Django

In this article we will write a code to display list of items as below

Here is the code for this.

{% extends "components/base.html" %}

{% block content %}
<div class="container-fluid">
{% if context %}
    <div class="row">
      {% for product  in context %}
          <div class="col rounded border border-light mt-3 ml-3 shadow">
              <img src={{product.productBaseInfoV1.imageUrls.400x400}} class="mt-3 mb-3" style="max-width: 200px;max-height: 200px;width: auto;height: auto;margin: auto;">
                <p class="card-text">{{product.productBaseInfoV1.title}}</p>
                <a href="{% url 'ecomm:details' product.productBaseInfoV1.productId %}" >Go to Details page</a>
          </div>
          {% if forloop.counter|divisibleby:4 %}
            </div>
            <div class="row">
          {% endif %}
      {% endfor %}
  </div
{% else %}
    <p>No tasks are available.</p>
{% endif %} 
</div>
{% endblock %}

This code is written for django but bootstrap and html part can be used for any other framework without any challenge.

You can remove following part without any impact.

          
{% if forloop.counter|divisibleby:4 %}
            </div>
            <div class="row">
          {% endif %}
{% endfor %}

My requirement was to have 4 item in a row and hence I had kept it but its not necessary and code will work flawlessly.

Creating Django models of an existing DB

Django makemiggrations and migrate command create tables from models as per details in models.py file but what if you have already created database ? creating model manually looking at DDL is cumbersome but you need not worry, Django provides an utility called as inspectdb to get this done.

How to use inspectdb ?

Step#1

inspectdb Introspects the database tables in the database pointed-to by the NAME setting.py and outputs a Django model. To use this utility cd to project home folder on terminal.

Step#2

To view models on terminal

python3 manage.py inspectdb

to generate file using inspectdb

python3 manage.py inspectdb > tempmodels.py

Please note above file will take database name from Django settings file and generate model.

If you want to generate model only for specific table

python3 manage.py inspectdb todo_task > tempmodels.py

Many times database have admin related tables whose models are not needed in your application so its a good idea to remove them from models.py or generate by providing table name as parameter.

Here is sample file generated using inspectdb

# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models


class TodoTask(models.Model):
    title = models.CharField(max_length=120)
    description = models.TextField()
    created_date = models.DateField(blank=True, null=True)
    due_date = models.DateField(blank=True, null=True)
    completed = models.BooleanField()
    completed_date = models.DateField(blank=True, null=True)
    note = models.TextField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'todo_task'

After this model is generated you need cross check it for data type, primary key and foreign key.

This takes care of make migrations part, you still have to run migrate command to create django admin related tables in database. Run following command once models.py file is ready

python3 manage.py migrate

Troubleshooting


$ python3 manage.py inspectdb
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.

login to postgres command prompt and execute following command


ecommpedia1=#  GRANT ALL PRIVILEGES ON TABLE ecomm_time TO ecommuser;
GRANT

This happens when you have created table after granting privileges on database to specific user. Since this table did not exists earlier, you need to GRANT privileges.

Django CRUD Application – Todo App – Tutorial

In this post we will build Todo Application using Django, before going thru this application, you should have basic knowledge of Django. If you want to review basics, please go thru following posts.

Let us create project

$ django-admin startproject mysite06
$ cd mysite06
/mysite06$ ls -la
total 16
drwxr-xr-x 3 conquistadorjd conquistadorjd 4096 Dec 31 10:15 .
drwxrwxrwx 15 conquistadorjd conquistadorjd 4096 Dec 31 10:15 ..
-rwxr-xr-x 1 conquistadorjd conquistadorjd 628 Dec 31 10:15 manage.py
drwxr-xr-x 2 conquistadorjd conquistadorjd 4096 Dec 31 10:15 mysite06
/mysite06$ python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

December 31, 2019 - 04:45:40
Django version 3.0.1, using settings 'mysite06.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[31/Dec/2019 04:45:44] "GET / HTTP/1.1" 200 16351
[31/Dec/2019 04:45:44] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423
[31/Dec/2019 04:45:45] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 85876
[31/Dec/2019 04:45:45] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 85692
[31/Dec/2019 04:45:45] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 86184
Not Found: /favicon.ico
[31/Dec/2019 04:45:45] "GET /favicon.ico HTTP/1.1" 404 1974

Now our basic project  is running

Now we need to create an app now.

python3 manage.py startapp todo

by default urls.py is not created in app, we need to create todo/urls.py manually.

from django.urls import path
from . import views

app_name = 'todo'
urlpatterns = [
    path('first/', views.index, name='index'),  
]

To have application urls accessible from main project, we need to add this urls.py with main project

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('todo/', include('todo.urls')),# Add this line
    path('admin/', admin.site.urls),
]

before doing further changes let us create sample view in todo/views.py we will edit this file later for details but we need to create this view to avoid any error while running intermediate command.


from django.shortcuts import render, get_object_or_404, redirect

def index(request):
    context = "temp"
    return render(request, 'todo/index.html',{'context':context})  

 

Changes  in settings.py file

Add application in settings file

INSTALLED_APPS = [
    'todo.apps.TodoConfig', # add this statement
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Here instead of ‘todo.TodoConfig’, even if you add ‘todo’ it will work but official method is to use ‘todo.TodoConfig’ so lets stick to that. You might wonder where ‘todo.TodoConfig’ is coming from. Just looke at app.py under todo app directory.

Now let us define templates directory as below

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR + '/templates/', # add this line
        ],
        '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',
            ],
        },
    },
]

and finally define static directory at the end of settings.py file.

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

We need to manually create static and templates directories. Directory structure created looks as below:

├── static
│   ├── css
│   ├── img
│   └── js
├── templates
│   └── todo

Creating model

Let us first create todoapp database and database user

$sudo su - postgres
[sudo] password for conquistadorjd: 
postgres@inspiron-3542:~$ psql
psql (10.10 (Ubuntu 10.10-0ubuntu0.18.04.1))
Type "help" for help.
postgres=# CREATE DATABASE todoapp;
CREATE DATABASE
postgres=# CREATE USER todoappuser WITH PASSWORD 'todoappsuser';  
CREATE ROLE
postgres=# ALTER ROLE todoappuser SET client_encoding TO 'utf8';
ALTER ROLE
postgres=# ALTER ROLE todoappuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE
postgres=# ALTER ROLE todoappuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE
postgres=# ALTER ROLE todoappuser SET timezone TO 'UTC';
ALTER ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE todoapp TO todoappuser;
GRANT
postgres=# \q
postgres@inspiron-3542:~$ exit
logout

Now we need to configure this database in our application


DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'todoapp',
        'USER': 'todoappuser',
        'PASSWORD': 'todoappuser',
        'HOST': 'localhost',
        'PORT': '',              
    }
}

Once this is done, we need to run following two commands


$ python3 manage.py makemigrations todo
Migrations for 'todo':
  todo/migrations/0001_initial.py
    - Create model Task
$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todo
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK
  Applying todo.0001_initial... OK

Let us look at database structure now


postgres=# \connect todoapp
You are now connected to database "todoapp" as user "postgres".
todoapp=# \dt
                     List of relations
 Schema |            Name            | Type  |    Owner    
--------+----------------------------+-------+-------------
 public | auth_group                 | table | todoappuser
 public | auth_group_permissions     | table | todoappuser
 public | auth_permission            | table | todoappuser
 public | auth_user                  | table | todoappuser
 public | auth_user_groups           | table | todoappuser
 public | auth_user_user_permissions | table | todoappuser
 public | django_admin_log           | table | todoappuser
 public | django_content_type        | table | todoappuser
 public | django_migrations          | table | todoappuser
 public | django_session             | table | todoappuser
 public | todo_task                  | table | todoappuser
(11 rows)

As you can see tables are now created in database.

Let us have this model accessible from admin and create some dummy data for development and unit testing.


from django.contrib import admin

from .models import Task

admin.site.register(Task)

Now create a admin user using following command

python3 manage.py createsuperuser

Let us run the server and login to admin from http://127.0.0.1:8000/admin and create some dummy data.

CRUD Application

Now our main activity of CRUD appliaction starts. You need to create urls for create, read,update and delete application along with matching views and templates.

url view Template Remark
/ index index.html
/newtask
newtask
newtask.html
Create new task
/<id> detail detail.html Task Details
/edit/<id> edit newtask.html Edit Task Details
/delete/<id> delete NA

(Once you click on delete, it will delete task and show index.html)

delete task

Here is code for todo/urls.py


from django.urls import path
from . import views

app_name = 'todo'
urlpatterns = [
    path('', views.index, name='index'),  
    path('/', views.detail, name='detail'),
    path('newtask/', views.newtask, name='newtask'),
    path('edit/', views.edit, name='edit'),
    path('delete/', views.delete, name='delete'),
]

todo/Views.py


from django.shortcuts import render, get_object_or_404, redirect
from .models import Task
from .forms import TaskForm

# Create your views here.

def index(request):
    context = Task.objects.all()
    return render(request, 'todo/index.html',{'context':context})    

def detail(request, task_id):
    task = get_object_or_404(Task, pk=task_id)
    return render(request, 'todo/detail.html', {'task': task})    

def newtask(request):
    form = TaskForm(request.POST or None)
    if form.is_valid():
        form.save()
        return redirect("/todo/")
    return render(request, 'todo/newtask.html', {'form':form})

def edit(request, task_id):
    task = get_object_or_404(Task, pk=task_id)
    print("**", task.title)
    form = TaskForm(request.POST or None, instance=task)
    if form.is_valid():
        form.save()
        return redirect("/todo/")
    return render(request, 'todo/newtask.html', {'form':form})


def delete(request, task_id):
    # print(request.method )  
    task = Task.objects.get(id=task_id)  
    task.delete()  
    return redirect("/todo/") 

index.html

{% extends "components/base.html" %}

{% block content %}

<div class="container-fluid">

{% if context %}

<tableclass="table">

<thead>

<tr>

<thscope="col">Title</th>

<thscope="col">Created Date</th>

<thscope="col">Status</th>

<thscope="col">Completed Date</th>

<thscope="col">Action</th>

</tr>

</thead>

<tbody>

{% for task in context %}

<tr>

<td><li><ahref="{% url 'todo:detail' task.id %}">{{ task.title }}</a></li></td>

<td>{{ task.created_date }}</td>

<td>{{ task.completed }}</td>

<td>{{ task.completed_date }}</td>

<td>

<ahref="/todo/edit/{{ task.id }}"><spanclass="glyphicon glyphicon-pencil">Edit</span></a>

<ahref="/todo/delete/{{ task.id }}">Delete</a>

</td>

</tr>

{% endfor %}

</tbody>

</table>

{% else %}

<p>No tasks are available.</p>

{% endif %}

<center><ahref="/todo/newtask"class="btn btn-primary">Add New Record</a></center>

</div>

{% endblock %}

newtask.html

{% extends "components/base.html" %}

{% block content %}

<h1>Edit</h1>

<form method="post">{% csrf_token %}

{{ form.as_p }}

<inputtype="submit"value="Submit"/>

</form

{% endblock %}

details.html

{% extends "components/base.html" %}

{% block content %}

<h5>Title : {{ task.title }}</h5>

<p>Description : {{ task.description }}</p>

<p>created_date : {{ task.created_date }}</p>

<p>due_date : {{ task.due_date }}</p>

<p>completed : {{ task.completed }}</p>

<p>note : {{ task.note }}</p>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<ahref="/todo/edit/{{ task.id }}"><spanclass="glyphicon glyphicon-pencil">Edit</span></a>

{% endblock %}

This application is available on Github for your reference.

How to use PostgreSQL with Django application

Before using PostgreSQL with Django, you need to create a database and database user. Please refer to following for creation of database and user using terminal.

$sudo su - postgres
[sudo] password for USER: 
postgres@inspiron-3542:~$ psql
postgres=#
postgres=# CREATE DATABASE polls;
postgres=# CREATE USER pollsuser WITH PASSWORD 'pollsuser';  
CREATE ROLE
postgres=# ALTER ROLE pollsuser SET client_encoding TO 'utf8';
ALTER ROLE
postgres=# ALTER ROLE polsuser SET default_transaction_isolation TO 'read committed';
ERROR:  role "polsuser" does not exist
postgres=# ALTER ROLE pollsuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE
postgres=# ALTER ROLE pollsuser SET timezone TO 'UTC';
ALTER ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE polls TO pollsuser;
GRANT
postgres=# \q

Now, make following changes in settings.py


DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'polls',
        'USER': 'pollsuser',
        'PASSWORD': 'pollsuser',
        'HOST': 'localhost',
        'PORT': '',            
    }
}

If we do not create database and user and run makemigrations command, you will get following error.

python3 manage.py makemigrations polls
Traceback (most recent call last):
  File "/home/conquistadorjd/.local/lib/python3.6/site-packages/django/db/backends/base/base.py", line 220, in ensure_connection
    self.connect()
  File "/home/conquistadorjd/.local/lib/python3.6/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/home/conquistadorjd/.local/lib/python3.6/site-packages/django/db/backends/base/base.py", line 197, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/home/conquistadorjd/.local/lib/python3.6/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/home/conquistadorjd/.local/lib/python3.6/site-packages/django/db/backends/postgresql/base.py", line 185, in get_new_connection
    connection = Database.connect(**conn_params)
  File "/home/conquistadorjd/.local/lib/python3.6/site-packages/psycopg2/__init__.py", line 126, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
psycopg2.OperationalError: FATAL:  database "polls" does not exist

This is because we have not created polls database. Now let us run makemigrations. makemigrations is like version control for database.

$ python3 manage.py makemigrations polls
No changes detected in app 'polls'

It says no changes detected in app because we have not added anything into polls/models.py. Let us add Questions and Choice model in this file.

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return self.question_text


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    def __str__(self):
        return self.choice_text   

After adding models definition, let us rune makemigrations command again


$ python3 manage.py makemigrations polls
Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice

Please note this just creates models files, tables are not yet created in database, we need to run migrate command to apply this to PostgreSQL database. Let us check if there are any tables in “polls” database

postgres=# \connect polls
You are now connected to database "polls" as user "postgres".
polls=# \dt
Did not find any relations.

As you can see, there are no tables in database “polls”. Let us run migrate command now


$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying polls.0001_initial... OK
  Applying sessions.0001_initial... OK

Now let us check if any tables exists in database polls.


polls=# \dt
                    List of relations
 Schema |            Name            | Type  |   Owner   
--------+----------------------------+-------+-----------
 public | auth_group                 | table | pollsuser
 public | auth_group_permissions     | table | pollsuser
 public | auth_permission            | table | pollsuser
 public | auth_user                  | table | pollsuser
 public | auth_user_groups           | table | pollsuser
 public | auth_user_user_permissions | table | pollsuser
 public | django_admin_log           | table | pollsuser
 public | django_content_type        | table | pollsuser
 public | django_migrations          | table | pollsuser
 public | django_session             | table | pollsuser
 public | polls_choice               | table | pollsuser
 public | polls_question             | table | pollsuser
(12 rows)

Now we have established connection with PostgreSQL. Now we need to do following.

  • Accessing these model from admin menu
  • Fetching and displaying data from database
  • Updating database

Accessing model from admin menu

To have these models accessed from admin menu, you simple have to register them for admin. This is very simple and can be done by adding below code into polls/admin.py file.


from django.contrib import admin

from .models import Question, Choice

# Register your models here.
admin.site.register(Question)
admin.site.register(Choice)

Here is the output. You can see both the models are visible from admin login now.

Fetching and displaying data from database

Now let us try to display some data from PostgreSQL tables to one of the view. You can add data directly into database or from admin menu. Its very easy to add data from admin menu, so I will use this method.

Here is how polls/views.py will look like

.
.

from .models import Question, Choice
.
.
def seventh(request):
    context = Question.objects.all()
    return render(request, 'polls/seventh.html' , {'context': context})

Please note that you need to import model names which you want to access from views. Django provide methods to access data from models.

View will look like this

{% extends "polls/base.html" %}
{% block content %}
{% if context %}
<ul>
{% for question in context %}
<li> {{ question}}</li>
{% endfor %}
</ul>
{% else %}
<p>No questions are available.</p>
{% endif %}
{% endblock %}

Here is the output

Updating database

Updating PostgreSQL is not very complex, its very similar to displaying data, you simply need to use different methods.


def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

also added following templates polls/details.html

{% extends "polls/base.html" %}
{% block content %}
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<inputtype="radio"name="choice"id="choice{{ forloop.counter }}"value="{{ choice.id }}">
<labelfor="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>
{% endblock %}

polls/results.html

{% extends "polls/base.html" %}
{% block content %}
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<ahref="{% url 'polls:detail' question.id %}">Vote again?</a>
{% endblock %}

You can find complete code here

How to use Bootstrap with Django

Bootstrap is the best CSS framework for websites. If you want to use Bootstrap on your Django project, you need to understand how Django templates work, you can refer to post Working with django templates before reading this further.

Post Working with django templates will give you basic understanding you need for this post, if you are already familiar with how django template inheritance work, you can continue with this post.

In this post we will create this Bootstrap 4 template using Django

 

As you can see, we have following three components

  • header
  • navbar
  • content
  • footer

Header, navbar and footer section will remain constant across websites. We will use template inheritance and include tags for building this web page.

Here is my folder structure

├── db.sqlite3
├── manage.py
├── mysite04
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── polls
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── __init__.py
│   │   └── __pycache__
│   │   └── __init__.cpython-36.pyc
│   ├── models.py
│   ├── __pycache__
│   │   ├── admin.cpython-36.pyc
│   │   ├── apps.cpython-36.pyc
│   │   ├── __init__.cpython-36.pyc
│   │   ├── models.cpython-36.pyc
│   │   ├── urls.cpython-36.pyc
│   │   └── views.cpython-36.pyc
│   ├── static
│   │   └── polls
│   │   ├── css
│   │   │   ├── bootstrap-solid.svg
│   │   │   └── pricing.css
│   │   ├── img
│   │   │   ├── favicons
│   │   │   │   ├── apple-touch-icon.png
│   │   │   │   ├── browserconfig.xml
│   │   │   │   ├── favicon-16x16.png
│   │   │   │   ├── favicon-32x32.png
│   │   │   │   ├── favicon.ico
│   │   │   │   ├── manifest.json
│   │   │   │   └── safari-pinned-tab.svg
│   │   │   └── tiger-beside-green-plants-standing-on-brown-land-during-145899.jpg
│   │   └── js
│   ├── templates
│   │   └── polls
│   │       ├── base.html
│   │       ├── fifth.html
│   │       ├── footer.html
│   │       ├── fourth.html
│   │       ├── head.html
│   │       ├── navbar.html
│   │       ├── second.html
│   │       ├── sixth.html
│   │       └── third.html
│   ├── tests.py
│   ├── urls.py
│   └── views.py

Here we have created base template and overriding templates using the logic explained inWorking with django templates . We need lots of static files while working with Bootstrap, check the post Working with Django Static files to understand how to work with static files. We have copied css, svg, img files using static files. We have also used couple of CDN based js and css files. you can find complete code at mysite04 github repository

Working with Django Static files

Any website need some standard files which are use across such as style sheet and JavaScript files, different style or JavaScript libraries.  Django provides  django.contrib.staticfiles to help you manage them.

By default django.contrib.staticfiles is included in INSTALLED_APPS, just make sure it there.

Following statement will be present at the end of settings.py file

STATIC_URL = '/static/'

Now create a folder name static under application (in current case polls). Create application folder under static again. The folder structure will look like below


├── mysite03
│   ├── db.sqlite3
│   ├── manage.py
│   ├── mysite03
└── polls
    └── static
    │   └── polls
    ├── templates
        └── polls

Here you can copy any file that you want to use. As a standard practice, you should create separate folders for css, js and img for stylesheets, JavaScript and images

Let me copy an image to static folder and display it on webpage. Here is the code

{% extends "polls/base.html" %}

{% load static %}

{% block content %}

<h>This is content Block from polls/fifth.html file </h>

<imgsrc="{% static 'polls/img/tiger-beside-green-plants-standing-on-brown-land-during-145899.jpg' %}"alt="My image"width="500"height="500">

{% endblock %}

Here is the output


Besides static files inside you application, you might have static files at project level or even at organization level, Django provides a facility to define these locations as static directory by adding following code in settings.py. You can define a list of directories (STATICFILES_DIRS) in your settings file where Django will also look for static files.

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    '/var/www/static/',
]

Here BASE_DIR is nothing but your project home directory. I have created another static folder and copied an image of an elephant there. Template page looks as below

{% extends "polls/base.html" %}

{% load static %}

{% block content %}

<h>This is content Block from polls/fifth.html file </h>

<imgsrc="{% static 'polls/img/tiger-beside-green-plants-standing-on-brown-land-during-145899.jpg' %}"alt="My image"width="500"height="500">

<imgsrc="{% static 'img/elephant-1822636_1280.jpg' %}"alt="My image"width="500"height="500">

{% endblock %}

here is the output

How django function based view works

What is view in Django

  • A view is a callable which takes a request and returns a response.
  • A view is a “type” of Web page in your Django application that generally serves a specific function and has a specific template.
  • In Django, web pages and other content are delivered by views. Each view is represented by a Python function (or method, in the case of class-based views).
  • Django will choose a view by examining the URL that’s requested (to be precise, the part of the URL after the domain name).
  • Each view is responsible for doing one of two things: returning an HttpResponse object containing the content for the requested page, or raising an exception such as Http404.
  • What views can do
    • Your view can read records from a database, or not.
    • It can use a template system such as Django’s – or a third-party Python template system – or not.
    • It can generate a PDF file, output XML, create a ZIP file on the fly, anything you want, using whatever Python libraries you want.

In this article, we will see how to work with function based views in Django. There is lot of debate on function based views vs class based views. Let us look at pros and cons of function based views

Pros

  • Simple to implement
  • Easy to read
  • Explicit code flow
  • Straightforward usage of decorators

Cons

  • Hard to extend and reuse the code
  • Handling of HTTP methods via conditional branching

If you look at pros and cons, its not a bad idea to stick with function based views again, it based on each developers perception and someone wants to use class based views, nothing wrong in that.

Let us create simple project which will render views using function based views.

How urls are structured


from django.urls import path

from . import views

# urlpatterns = [
#     path('', views.index, name='index'),
#     path('details', views.details, name='index'),
#     path('vote', views.vote, name='vote'),
# ]
app_name = 'polls'
urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('/', views.detail, name='detail'),
    # ex: /polls/5/results/
    path('/results/', views.results, name='results'),
    # ex: /polls/5/vote/
    path('/vote/', views.vote, name='vote'),
]

And function based views are defined as below


from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.http import Http404
from django.urls import reverse
from .models import Question, Choice

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

What is the difference between render and HttpResponse
render
Combines a given template with a given context dictionary and returns an HttpResponse object with that rendered text.  Render is basically a simple wrapper around a HttpResponse .

HttpResponse
you can use HttpResponse to return others things as well in the response, not just rendering templates.
If you are making AJAX call, you can return JSON as below

return HttpResponse(jsonObj, mimetype='application/json')

httpresponseredirect


return HttpResponseRedirect("http://example.com/"):

It will return an HTTP status code 302 [redirect] along with the new URL. This should be used only to redirect to another page (e.g. after successful form POST)

redirect

return redirect('https://example.com/')

redirect gives the HttpResponseRedirect for the argument you have passed.

render_to_response

Reference:

https://docs.djangoproject.com/en/3.0/topics/http/shortcuts/

 

Working with django templates

Django ships with a built-in backend for its own template system – the Django Template Language (DTL).

the most basic way you can use Django’s template system

  1. Create a Template object by providing the raw template code as a string.
  2. Call the render() method of the Template object with a given set of variables (the context).

Django makes it possible to separate python and HTML, the python goes in views and HTML goes in templates. To link the two, Django relies on the render function and the Django Template language.

This function takes three parameters −

  • Request − The initial request.
  • The path to the template − This is the path relative to the TEMPLATE_DIRS option in the project settings.py variables.
  • Dictionary of parameters − A dictionary that contains all variables needed in the template. This variable can be created or you can use locals() to pass all local variable declared in the view

Let us update following in polls/urls.py

path('first/', views.first, name='first'),
path('second/', views.second, name='second'),

If you want to display simple message without using template, it can be done using as below

from django.http import HttpResponse

def first(request):
    return HttpResponse("Hello, world. You're at the polls index.")

If you want to implement same thing using dejango templates, you need to use code as below

add this into views.py

from django.shortcuts import render

def second(request):
    return render(request, 'polls/second.html')

create a template in this path ‘template/polls/second.html’

Hello, world. You're at the polls index. This is from templates/polls/second.html

and here is the output from http://127.0.0.1:8000/polls/first/

and here is the output from http://127.0.0.1:8000/polls/second/

Working with Django templates

When we create website, basic rule that we follow is DRY (Dont repeat yourself). In any website, few things are common in all pages, like navbar and footer. In such cases, in make sense to have this coded at one place and include it wherever required, this way, if we have to make any change, we need to change at only one place. In Django tenplates this is achived using template inheritance

Template inheritance

Template inheritance is one of the most useful feature of django templates. Template inheritance allows you to build a base “skeleton” template that contains all the common elements of your site and defines blocks that child templates can override.

Here is the base template. Its created in polls/templates/polls directory


<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}Mysite02 Django Tutorial{% endblock %}</title>
</head>

<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/polls/first/">first</a></li>
<li><a href="/polls/second/">second</a></li>
<li><a href="/polls/third/">third</a></li>
<li><a href="/polls/fourth/">fourth</a></li>
</ul>
{% endblock %}
</div>

<div id="content">
{% block content %}
<h>This is content Block from base file </h>
{% endblock %}
</div>
{% block footer %}
<footer>
(C) all rights resevered. 2019
</footer>
{% endblock %}
</body>
</html>

Here is overriding template

{% extends "polls/base.html" %}

{% block content %}
    This is content Block from polls/fourth.html file 
{% endblock %}

{% block footer %}
    This is footer Block from polls/fourth.html file 
{% endblock %}

this is comment outside block from polls.fourth.html

Here is how base template looks like

Here is how template after over riding looks like

Here you can see content block and footer block is replaced by the template.

Also note that content outside of the block from overriding template is not displayed.

Accessing methods

Most method calls attached to objects are also available from within templates. This means that templates have access to much more than just class attributes (like field names) and variables passed in from views. For example, the Django ORM provides the “entry_set” syntax for finding a collection of objects related on a foreign key. Therefore, given a model called “comment” with a foreign key relationship to a model called “task” you can loop through all comments attached to a given task like this:

 

Getting started with django with python3 and ubuntu

Tag line for django says “The web framework for perfectionists with deadlines.” and most would agree. django is rediculously fast to implement, its fully loaded with lots of utilitis, its secure and at the same time its highly scalable. some of the most popular sites like instagram, pinterest are built with django.

Let us get our hands dirty by getting started with django instead of talking about django features.

Which django version should I use ?

Here are the details about djnago release. you can choose latest version, however, I will stick with LTS version

Which Python version should I use ?

This is the most common question and following table with clarify your query

Django version Python versions
  1.11 2.7, 3.4, 3.5, 3.6
  2.0 3.4, 3.5, 3.6, 3.7
  2.1,2.2 3.5, 3.6, 3.7

In this tutorial we are going to use python version 3.6 (3.6.4 to be very specifuc) and latest django version 3.0.1

Installation

If you want to install specific version use following

$ pip3 install Django==3.0.1

Alternative way to install Django is as below

$ sudo python3 -m pip install django
Collecting django  
  Downloading Django-X.X.X-py3-none-any.whl (7.1MB)
    100% |████████████████████████████████| 7.1MB 186kB/s 
Requirement already satisfied: pytz in /usr/local/lib/python3.6/site-packages (from django)  
Installing collected packages: django  
Successfully installed django-X.X.X  

Checking the installed version

Simple way to check is using below command

$python3 -m django --version
3.0.1

another way

~$ python3
Python 3.6.4 (default, Jan 13 2018, 12:02:51)  
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.  
>>> import django
>>> print django.get_version()
  File "<stdin>", line 1
    print django.get_version()
               ^
SyntaxError: invalid syntax  
>>> print(django.get_version())
X.X.X  

Creating django project

Project is created using following command

$django-admin startproject mysite

This will create folder mysite having required files. Now let us start the application by running server.

$ python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

December 28, 2019 - 07:55:49
Django version 3.0.1, using settings 'mysite01.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.  

Now your website is up and running

By default server status at port 8000. You can change port using following command

$python3.6 manage.py runserver 8080

Creating an app

The term application describes a Python package that provides some set of features. Applications may be reused in various projects. The term project describes a Django web application. The project Python package is defined primarily by a settings module, but it usually contains other things.Applications include some combination of models, views, templates, template tags, static files, URLs, middleware, etc. They’re generally wired into projects with the INSTALLED_APPS setting and optionally with other mechanisms such as URLconfs, the MIDDLEWARE setting, or template inheritance.

$ python3 manage.py startapp polls

This will create a sub-directory as below

├── mysite01
│   ├── db.sqlite3
│   ├── manage.py
│   ├── mysite01
│   │   ├── asgi.py
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   ├── __init__.cpython-36.pyc
│   │   │   ├── settings.cpython-36.pyc
│   │   │   ├── urls.cpython-36.pyc
│   │   │   └── wsgi.cpython-36.pyc
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   └── polls
│       ├── admin.py
│       ├── apps.py
│       ├── __init__.py
│       ├── migrations
│       │   └── __init__.py
│       ├── models.py
│       ├── tests.py
│       └── views.py

Now to have our very basic application working, let us create urls.py file under folder polls and add below code

from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    path('first/', views.first, name='first'),
]

Please note “/’ after “first” url name.

We have updated url structure now let us code the view to be displayed if these urls are accessed. Update following code in polls/views.py

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

def first(request):
    context = "Test Data"
    return render(request, 'polls/first.html', {'context': context})

As you see, we are displaying very basic message. This will give you basic idea about hor urls and views work. Now before you run the server again we need to inform our project about the application ‘polls’ by updating settings.py file. Update following in settings.py


INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Also update main project urls.py to include polls/urls.py. any url starting with domain-name+polls will be served from ‘polls/urls.py’


from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/',  include('polls.urls')),
    path('admin/', admin.site.urls),
]

Now let us run the server again

$ python3 manage.py runserver

Now you can access this website at below path
http://127.0.0.1:8000/polls/

http://127.0.0.1:8000/polls/first/