Create a contact form with Django and SQLite

Create a contact form with Django and SQLite

·

31 min read

Welcome back. In this post, we'll be using HTML template and Django to create a contact form with Django and SQLite. We will also try to see all the listings in the admin panel.

This is a basic Django web application focusing on some basic crud and we will be saving the data in an SQLite database.

Why Django?

Django is a free and open-source python-based web framework for creating dynamic web apps. Some popular sites that use Django are Instagram, Mozilla, The Washington Post, etc.

The Web framework for perfectionists with deadlines | Django!

We will use bootstrap for styling the form. For using the bootstrap, we can use either CDN or we can download the CSS files from the bootstrap site.

Below is the code for a simple Form made using bootstrap.

<!doctype html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
    integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

  <title>Contact</title>
</head>

<body>

  <h1 class="text-center my-5">Contact Form</h1>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-lg-6">
        <form action="" method="POST">
          <div class="form-group">
            <label for="name">Full Name</label>
            <input type="text" class="form-control" name="name" id="name" placeholder="Your Name">
          </div>
          <div class="form-group">
            <label for="email">Email address</label>
            <input type="email" name="email" class="form-control" id="email" placeholder="name@example.com">
          </div>
          <div class="form-group">
            <label for="message">Your message</label>
            <textarea class="form-control" name="desc" id="message" rows="3"></textarea>
          </div>
          <input type="submit" class="btn btn-primary" value="Submit">
        </form>
      </div>
    </div>
  </div>

</body>

</html>

Here we have used the CDN link for including bootstrap in our HTML code.

To submit our form, I have used input type submit but you can also use the button tag in place of input type submit.

In place of form action, we will try to submit the form in the same path that will be the root URL and we will handle form submission in our code.

This code will help us in creating a form which we will be using it in the templates of our Django project.

Now we are done with the HTML, and now, we can start with the Django project setup.

Note: Before starting with Django, we will create a virtual environment where we will have all our files and packages stored.

Creating a Virtual Environment

Before starting with creating a virtual environment, we need to install a package that will help us in creating the environment.

A virtual environment is a tool to create a private workspace for a Python application.

One of the major advantages of using a virtual environment is the ability to install modules locally.

You can also export a working environment and execute Python programs in that environment.

Make sure you have the latest version of Python installed on your system.

Now you are done with that, and we can now move to the next step of creating the virtual environment.

We have many python packages for creating the virtual environment. Here I'll be using the pipenv.

The main reason for using pipenv is that its commands are the same on all the platforms.

To install this package on your system, copy the below command and paste it in your terminal.

# for mac and Linux users
pip3 install pipenv

# for windows users
pip install pipenv

terminal This package automatically creates and manages virtual env for your projects, as well as adds/remove packages from your pipfile as you install/remove packages.

Now this package is installed on your system. The next step is to create a new virtual environment.

For creating a new virtual environment, let's first create a new directory which we are going to use it as our virtual environment for this project, and name it myenv for now.

You can name it anything you want but for now, keep it the same.

# command to create a new directory
mkdir myenv

# command to open that directory
cd myenv

terminal mkdir myenv will help you in creating a new directory, and cd myenv will help you to change the working directory to myenv.

Now we are inside our folder where we want to create the env, run the following command to create a new environment inside myenvdirectory.

# command to create a new virtual env
pipenv shell

terminal This command will create and activate a new virtual environment in your present directory, and now you are ready to install new packages in your env.

But wait. You can't install any packages simply by writing pip install.

Since you are inside the virtual env of pipenv, you need to use a slightly modified command to install any packages.

Now don't get panic here. The command is not so hard to keep it in mind.

Earlier we used to write pip install xyz-package, but now we need to write pipenv install xyz-package.

Not so major change but worth keeping in mind.

Now we are ready with the package installation, and we can head over to install Django.

Head over to the official documentation of Django by clicking here.

Copy the following command and paste it into your terminal, and it will help you to install Django.

# this will install Django in your virtual env
pipenv install Django

terminal To check the installed packages, type the following command, and it will list all the installed packages in your terminal.

pip freeze

terminal You'll see the list of all the installed packages in your terminal, and it will also have Django in it.

Note: The Django which we tried to install is installed only in this virtual env and not globally. So you cannot use it from anywhere.

This also means if you need to create a new Django project, you need to create a new virtual env and install Django from fresh again.

Creating a Django project

Now we are installed with Django, and we can move on to creating a new project with Django.

To create a new Django project, copy the following command and paste it in your terminal.

django-admin startproject contactform

terminal Here the contactformis the name of our project.

You can now see that django-admin has created some files for us to work on. We are not going to change anything as of now.

Next is to change the directory to contactform and start our server.

# to change the working directory
cd contactform

# to see the files and folders
ls

terminal Here you can see the folders and files of our contactform.

ls helps us to list all the files and folders inside our current directory. Inside this directory, we have a manage.py file, a db.sqlite3 and a directory name contactform.

db.sqlite3 is optional. It may get created or maybe not, but don't worry. We will seel it later.

Now we have our project created, and we can start our server now and have a look at it in our browser.

Start Server

To start the server, you can run the following command.

# for mac and Linux users
python3 manage.py runserver

# for windows users
python manage.py runserver

terminal Now go to http://127.0.0.1:8000/, and you'll see you Django project up and running. This is the default page of the index. You'll learn how to serve your own HTML page later in this post.

Since we are done with the project setup and our server is started now, we can move on to creating our app for the contact form.

You might be thinking that we have already created a project above using the django-admin command, then why are we going to create a new app.

The answer is simple.

In Django, everything is an app. Whether you want to create a blog, or you want to create e-commerce. Everything is an app.

Create a contact app

Now to create a contact app, we'll run the following command in the same directory.

Don't get confused with the directory hierarchy. They are very straight forward. You just need to keep things in mind the way they are.

For running these commands, you don't need to terminate your running server. You can open a new terminal in the same directory and then you can activate the environment there and then you can use any of these commands without any problem.

# for mac and Linux 
python3 manage.py startapp contact

# for windows
python manage.py startapp contact

terminal Now you have your contact app ready, and we can now move to the next part.

But before moving forward, let's revise the points as they are very confusing at the beginning.

  1. First, we installed a python package that helps us to create our virtual env.
  2. Next, we created a new folder named myenv inside which we created our virtual environment using pipenv shell.
  3. After that, we installed Django in our virtual env.
  4. We then created a new Django project named contactform using django-admin startproject contactform
  5. Next, we changed our directory and opened contactform, and inside that, we have our project files from contactform and a manage.py file.
  6. We then started the server so that our project will be live and can be seen in the browser.
  7. And after this, we created a new app for the contact form.

Handling URLs

To handle all the URLs, Django has URL dispatcher which handles all the endpoints. This means that, whether you want to go the root URL or at /admin, everything is being handled by Django URL dispatcher.

Here we want to handle our URLs by the urls.py of our contact app but we don’t have any urls.py file in our app’s directory. So we need to create one.

Create a new urls.py file inside the contact app directory.

Open your contactform’surls.py file and import includefrom django.urls. Now inside urlpatterns array, add path('', include('contact.urls')) as shown in the below code.

"""contactform URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
# here I have imported include from django.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('contact.urls'))
]
#I have also added a new path which will be handled by the urls.py file of my contact directory

Now what is happening here is we are telling our project’s urls.py file that if any user comes with the request of root URL or /, tell him to meet contact’s urls.py.

Now inside app's urls.py, add the following code.

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

# we need to import views from our contact app
from contact import views

urlpatterns = [
  path('', views.index, name='index'),
]

When it meets contact’s urls.py file, the URL dispatcher of the contact app checks for matching path. If any path matches with the path defined in the views.py file our app, then the request is served with the defined response.

We have named our defined path as name='index'. This is up to you what you want to name the path which you have defined.

Since we don’t have any path defined inside our views.py file, we need to define a path in our app’s views.py file.

Below is the code that will help us to serve any response if the request is for /. Replace the code of views.py file with the below code.

from django.shortcuts import render, HttpResponse

# Create your views here.
def index(request):
  return HttpResponse('Working')

Here I have not served any static Html file but I'll do that later in this post.

Similarly, you can create some other path inside your app's urls.py file and you can define the method that will handle the request inside the views.py file.

These methods take the request as a parameter and don’t forget to import HttpResponse from django.shortcuts.

Now, check your root endpoint, it will show your HttpResponse which you’ve created and not the default response of Django.

Here we have used HttpResponse to render some basic string response inside views.py file. The views.py also has import render but you might be thinking why to import it when we are not using it.

To use render, we need to define the path of templates directory first and then only we can use it to render our templates.

So our next step is to create two new directories, static and templates in the same directory where manage.py, app’s file and project’s files are stored.

The Django application serves static files like static CSS files or the JS files or any other kind of static files from its static directory.

To use return render request, we need some kind of HTML template, and that needs to be stored inside the templates directory.

Keep one thing in mind, people can see your static files or the files stored in the static directory but they can’t see your python code. So don’t keep any file that has some important details inside the static directory.

Now open your settings.py file and paste the following code just below the STATIC_URL = '/static/' as shown in the below code.

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'

# Copy from below
# Static directory path
STATICFILES_DIRS = [
    BASE_DIR / "static",
    '/var/www/static/',
]

You just need to paste the STATICFILES_DIRS and not the complete code.

You can also read more about managing static files from the link given below.

Managing static files

Now let’s try to serve any static file from the static directory. Copy any image and paste it in the static directory and open its path in the browser.

http://127.0.0.1:8000/static/demo.txt

I’ve created a demo.txt file and stored it in a static directory. Similarly, you can store any file and it can be served using the same static URL. Make sure you are using the correct URL.

Templates path configuration

Our next step is to configure the path of templates directory to use our HTML templates. You just need to copy the following code BASE_DIR / "templates" and paste it in the TEMPLATES array’s 'DIRS': [ BASE_DIR / "templates" ], or as shown in the code below.

# See the code in line number 7
# Make sure your code looks same as mine

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

This will configure the path of the template directory and you are now ready to serve your Html templates.

Now create a new index.html file inside your templates directory. Now open your views.py file and here we are going to use return render(request, ‘index.html’).

Below is the code to use it in the views.

from django.shortcuts import render

# Create your views here.
def index(request):
  return render(request, 'index.html')

We can now remove HttpResponse as we no longer use it in our views.py file.

Using variables with templates

We can also use variables in our Html template to send any information that we want to show on our Html template.

To use any variable, we use {{ variable_name }} in our template.

<!-- Copy the following code and paste it in your HTML template -->
<p>I am {{ name }}</p>

And inside my views.py file, I can pass this variable with template.

from django.shortcuts import render

# Create your views here.

def index(request):
  # create your variable here
  context = {
    'name' : 'Pratik'
  }
  # pass it here with the html template
  return render(request, 'index.html', context)

Django Admin Panel

Now you are done with the templating, we can now move to the admin panel. Visit http://127.0.0.1:8000/admin/ and you’ll see a login form. Try login and it will give you an error no such table: auth_user.

This error is because we don’t have any table in our database as of now. To make the changes in our database, we need to run two terminal commands.

# fro Mac and Linux users
python3 manage.py makemigrations

# for Windows users
python manage.py makemigrations

terminal And the second command

# fro Mac and Linux users
python3 manage.py migrate

# for Windows users
python manage.py migrate

terminal When you’ll run the first command, this will not show any changes. But when you’ll run the second command, you’ll see some changes in the terminal.

This is because Django uses some default schema or table for user authentication. Now when you try to log in, this time it will not show the same error.

It will ask you to enter the correct username and password.

Creating superuser

We know why our id and password is wrong as there is no user registered by default.

So we need to create a new superuser. Below is the command to create a new superuser. This will ask you for some basic information and a new superuser will be created.

# fro Mac and Linux users
python3 manage.py createsuperuser

# for Windows users
python manage.py createsuperuser

terminal You can now use that credentials to log in to the admin panel. All our database model, information stored in database can be seen in the admin panel.

You might be thinking how to change the title and header of our admin panel and name it as per our application as Django administration doesn’t look professional. This can be changed by adding these code in your project’s urls.py file.

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

# Add these three lines of code in your urls.py file same as I did
admin.site.site_header = "Contact Admin"
admin.site.site_title = "Contact Admin Portal"
admin.site.index_title = "Welcome to Contact Portal"

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

Note: These codes need to be pasted in your project level urls.py file and not in app-levelurls.py file.

Now check your admin panel. This will look much better now after the name change.

Model class

We are done with the form setup. Let us now create a model class for saving the data from our contact form.

But before that, let’s understand what is a model?

A database model is a type of data model that determines in which manner data can be stored and manipulated.

Ex: Relational model

Now inside your models.py file, let us create a Contact model. We need to import models from django.db and then write our model class. Copy the below code to create a contact model.

from django.db import models

# Create your models here. 
# Note that I have used an uppercase letter for class name

class Contact(models.Model):
  name = models.CharField(max_length=122)
  email = models.CharField(max_length=120)
  desc = models.TextField()
  date = models.DateField()

Now let us run makemigrations command to register all the changes in the model.

# fro Mac and Linux users
python3 manage.py makemigrations

# for Windows users
python manage.py makemigrations

This command will tell Django to make a note of all the changes I’ve made in the model.

This will not make any new table or make any changes in the database. This will only register the changes that are made and these changes will come into effect when we run the migrate command.

But for your surprise, the makemigrations will still show that No changes detected.

But how is this possible? We have made changes in the model, but this is still showing that the model is still the same.

The reason is simple. We have made changes, but we have not registered our model yet to make the changes.

To register our model, open your admin.py file and import Contact from contact.models. Or you can paste the following code in your admin.py file.

from django.contrib import admin
from contact.models import Contact  

# Register your models here.
admin.site.register(Contact)

This will register your model with Django but you need to register your app in INSTALLED_APPS inside settings.py file.

Go to your apps.py file inside your contact app directory, copy the name of the class and go to settings and inside INSTALLED_APPS, paste it there as ‘contact.apps.ContactConfig’ or as shown in the code below.

# here as shown in the first line inside the array

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

Now when you’ll try to run the makemigrations command, this will make some changes that you will see in your terminal and inside your project directory.

# fro Mac and Linux users
python3 manage.py makemigrations

# for Windows users
python manage.py makemigrations

And when you’ll run migrate command, this will generate a new table named contact that can be seen in the admin panel.

# fro Mac and Linux users
python3 manage.py migrate

# for Windows users
python manage.py migrate

Visit your admin panel and there you'll see the contact table like this.

admin.png

We can save data from the admin panel directly but we are not going to do that here. We will submit our form data using the Html form we have created in our HTML file.

To accept the data for our model field, we need to write our logic inside our views.py file and we are good to go.

Now inside our views.py file, we need to paste the following code.

from django.shortcuts import render
from datetime import datetime
from contact.models import Contact

# Create your views here.
def index(request):
  if (request.method == 'POST'):
    name = request.POST.get('name')
    email = request.POST.get('email')
    desc = request.POST.get('desc')
    contact = Contact(name=name, email=email, desc=desc, date=datetime.today())
    contact.save()
  return render(request, 'index.html')

Let us understand what we are doing with the following code.

  1. We have imported our model class and datetime.
  2. Next, we are going to check if the request is GET or POST.
  3. If the request is GET, we simply return our Html template.
  4. If the request is POST, we are fetching name, email, desc from the form and we are passing it in the constructor of our model class and in this way, they will be stored in our table when we call the save method.

We can now try to submit our form. But wait, now this is showing that CSRF verification failed. Request aborted.

What does that mean?

The CSRF middleware and template tag provides easy-to-use protection against Cross-Site Request Forgeries. You can learn more about it here.

Cross Site Request Forgery protection

This error occurred because of missing csrf token. We can add this code {% csrf_token %} inside our <form> tag or as shown below.

<form action="/" method="POST">
  {% csrf_token %}
  <div class="form-group">
    <label for="name">Full Name</label>
    <input type="text" class="form-control" name="name" id="name" placeholder="Your Name">
  </div>
  <div class="form-group">
    <label for="email">Email address</label>
    <input type="email" name="email" class="form-control" id="email" placeholder="name@example.com">
  </div>
  <div class="form-group">
    <label for="message">Your message</label>
    <textarea class="form-control" id="message" name="desc" rows="3"></textarea>
  </div>
  <input type="submit" class="btn btn-primary" value="Submit">
</form>

If you want to change the name of user who sent you the mail, you can do it by adding this small code inside your models.py file.

from django.db import models

# Create your models here. 
# Note that I have used an uppercase letter for class name

class Contact(models.Model):
  name = models.CharField(max_length=122)
  email = models.CharField(max_length=120)
  desc = models.TextField()
  date = models.DateField()
  # code to show name of the person who sent you the message
  def _str_(self):
    return self.name

Now, the admin panel will show you the name of the sender who has sent you the message.

You can download the code from my GitHub repo from the blow link.

ThePratikSah on GitHub

Well, this was all for today. I know this post is way too long but this is an in-depth guide for Django contact form.

Thanks for your time. If you found this post helpful😊, please share this with your friends.

If you stuck into some kind of problem, feel free to comment below. I'll be happy to help you🙂.

Did you find this article valuable?

Support Pratik Sah by becoming a sponsor. Any amount is appreciated!