Django — Step 13

Posting Data To the Database From a Form

Brian Mayrose
7 min readAug 8, 2019

This is a continuation from step 12:

So far we can view the data we have in the database either from the admin panel or from the rendered template on the client-side of the project.

At this point, we can use the admin panel to add content to the database, but it might be advantageous to have the ability to upload content from a form that is rendered in a template.

Django has a great module called Forms. We can use Django Forms to make as many forms as needed inside of a python file then use and reuse the same HTML template for all of the forms created.

The view function for each form will be where the specific form is called, multiple views can share the same template.

Here is the official Django Forms documentation:

Let’s get started with the implementation of Forms in our current project. The first step is to create a forms.py file within the display_1 directory.

At the top of forms.py, we need to import forms from Django

from django import forms

Then we create a class definition. I will name it Gallery1_Form for this example:

class Gallery1_Form(forms.Form):

We can follow the item model from the models.py file to make the form. The first input in the model is title.

title = models.CharField(max_length=200)

The structure Django Forms accepts for text input is very similar to what our model uses, the main difference is we call forms instead of models:

title = forms.CharField(max_length=200)

The second item we need in our form is the description_1 item. Just like the model we don't set a max_length. And we also want a larger area to type the description in so we use a Django Form widget called Textarea:

description_1 = forms.CharField(widget=forms.Textarea)

Here is the official documentation on the different widgets offered by Django Forms:

We follow the same pattern for the next 2 descriptions, but we add the required=False argument, this will prevent the form rendered from forcing the client to add an entry in these 2 fields:

description_1 = forms.CharField(widget=forms.Textarea)
description_2 = forms.CharField(widget=forms.Textarea, required=False)
description_3 = forms.CharField(widget=forms.Textarea, required=False)

We follow the description fields with the reference_link field. Instead of a CharField it uses the URLField:

reference_link = forms.URLField(required=False)

You can go over all the available input fields offered by Django Forms:

After the URL field, we need to add the fields for the photos. Following the item model, we have a mandatory main_photo and 6 optional photos we can upload.

We use the Django Forms FileField as the input for these:

    photo_main = forms.FileField()
photo_1 = forms.FileField(required=False)
photo_2 = forms.FileField(required=False)
photo_3 = forms.FileField(required=False)
photo_4 = forms.FileField(required=False)
photo_5 = forms.FileField(required=False)
photo_6 = forms.FileField(required=False)

Next is the is_published BooleanField, we set the required argument to False allowing the form to post items that won't be published:

is_published = forms.BooleanField( required=False, )

The final field is the list_date.

list_date = forms.DateTimeField( initial=datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S"))

Similar to the model Django Forms uses the DateTimeField. The key to this field is the initial argument,

initial=datetime.datetime.now().strftime(“%Y-%m-%d %H:%M:%S”),

This will automatically populate the field in the form with the current date and time.

Here is the complete forms.py file with the Gallery1_Form Class definition:

from django import formsclass Gallery1_Form(forms.Form):
title = forms.CharField(label='the title', max_length=200)
description_1 = forms.CharField(widget=forms.Textarea)
description_2 = forms.CharField(widget=forms.Textarea, required=False)
description_3 = forms.CharField(widget=forms.Textarea, required=False)
reference_link = forms.URLField(required=False)
photo_main = forms.FileField()
photo_1 = forms.FileField(required=False)
photo_2 = forms.FileField(required=False)
photo_3 = forms.FileField(required=False)
photo_4 = forms.FileField(required=False)
photo_5 = forms.FileField(required=False)
photo_6 = forms.FileField(required=False)
is_published = forms.BooleanField( required=True, )
list_date = forms.DateTimeField(initial=datetime.date.now())

Save this file.

Now open display_1/views.py so we can write the view that will handle the upload logic and the rendering of the form.

First import HttpResponseRedirect and the Gallery1_Form at the top of the views.py file:

from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponseRedirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django import forms
from .models import item
from .forms import Gallery1_Form

Then, following the documentation, we want to create a basic outline of the view:

def gallery1_post(request):

if request.method == 'POST':

form = Gallery1_Form(request.POST, request.FILES)

if form.is_valid():
return HttpResponseRedirect('/display_1')
else:
form = Gallery1_Form()
return render(request, 'display_1/formForGallery1.html', {'form': form})

I am calling this view definition gallery1_post.

Within the view, we start with an if statement that is called if the gallery1_post definition is called along with a POST request which will happen when submitting the form.

we define form as the Gallery1_Form class we defined in forms.py. The request.POST argument is mandatory and request.FILES is optional, but needed in this example because we are uploading the image files:

form = Gallery1_Form(request.POST, request.FILES)

The first return in the view will be called if the form throws no errors:

if form.is_valid():

redirecting the client to the display_1 path.

return HttpResponseRedirect('/display_1')

Following the return is the else statement. This is called if the client is just navigating to the form, setting form to a blank rendering of the Gallery1_Form from forms.py:

form = Gallery1_Form()

Then calls a return render of the form template:

return render(request, 'display_1/formForGallery1.html', {'form': form})

Save the views.py file and create a formForGallery1.html file in the templates/display_1 directory.

Put this code in formForGallery1.html:

{% extends 'base.html' %}{% block content %}<section>
<h1 class="display-4 mb-4 text-center">
Gallery 1 post form
</h1>
<div style='width:100px;margin-left:10px;'>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" class="btn btn-primary">
</form>
</div>
</section>
{% endblock %}

We are extending the base template. We have a basic title. Then we have a div with some basic inline styling we can replace later.

Inside the div are form tags. The action is left blank. The view called will implicitly set the action. We must have the method set to post. We also need to add enctype=”multipart/form-data”, this allows the Django form to accept the image files we upload:

<form action="" method="post" enctype="multipart/form-data">

Inside the form tags, we add the Jinja csrf_token tag which prevents cross-site registry forgery.

{% csrf_token %}

We follow this with the form tag

{{ form }}

and a submit button

<input type="submit" value="Submit" class="btn btn-primary">

Save the formForGallery1.html file and open up the display_1/urls.py file.

Add the path to the gallery1_post view:

path('g1_post', views.gallery1_post, name='g1_post'),

Save the file, start the development server

python manage.py runserver

and navigate to http://127.0.0.1:8000/display_1/g1_post

You should see the form we created with the date and time automatically populated:

At this stage, the form could use some more styling, but first, we need to add logic to the gallery1_post view to get the form to actually upload content to the database.

Open up the display_1/views.py file again.

directly below we need to add the cleaned_data from each field in the form:

        if form.is_valid():            title = form.cleaned_data['title']
description_1 = form.cleaned_data['description_1']
description_2 = form.cleaned_data['description_2']
description_3 = form.cleaned_data['description_3']
reference_link = form.cleaned_data['reference_link']
photo_main = form.cleaned_data['photo_main']
photo_1 = form.cleaned_data['photo_1']
photo_2 = form.cleaned_data['photo_2']
photo_3 = form.cleaned_data['photo_3']
photo_4 = form.cleaned_data['photo_4']
photo_5 = form.cleaned_data['photo_5']
photo_6 = form.cleaned_data['photo_6']
is_published = form.cleaned_data['is_published']
list_date = form.cleaned_data['list_date']

Then create a variable that takes the data and creates a database object with the name_of_database.objects.create function. I called the variable display_1_item in this example:

            display_1_item = item.objects.create(

We call each item from our form and database model within the create function:

            display_1_item = item.objects.create(
title=title,
description_1=description_1,
description_2=description_2,
description_3=description_3,
reference_link=reference_link,
photo_main=photo_main,
photo_1=photo_1,
photo_2=photo_2,
photo_3=photo_3,
photo_4=photo_4,
photo_5=photo_5,
photo_6=photo_6,
is_published=is_published,
list_date=list_date,
)

We follow the create function with the save function:

           display_1_item.save()

Here is what the entire gallery1_post view should look like now:

def gallery1_post(request):

if request.method == 'POST':

form = Gallery1_Form(request.POST, request.FILES)

if form.is_valid():
title = form.cleaned_data['title']
description_1 = form.cleaned_data['description_1']
description_2 = form.cleaned_data['description_2']
description_3 = form.cleaned_data['description_3']
reference_link = form.cleaned_data['reference_link']
photo_main = form.cleaned_data['photo_main']
photo_1 = form.cleaned_data['photo_1']
photo_2 = form.cleaned_data['photo_2']
photo_3 = form.cleaned_data['photo_3']
photo_4 = form.cleaned_data['photo_4']
photo_5 = form.cleaned_data['photo_5']
photo_6 = form.cleaned_data['photo_6']
is_published = form.cleaned_data['is_published']
list_date = form.cleaned_data['list_date']
display_1_item = item.objects.create(
title=title,
description_1=description_1,
description_2=description_2,
description_3=description_3,
reference_link=reference_link,
photo_main=photo_main,
photo_1=photo_1,
photo_2=photo_2,
photo_3=photo_3,
photo_4=photo_4,
photo_5=photo_5,
photo_6=photo_6,
is_published=is_published,
list_date=list_date,
)

display_1_item.save()
return HttpResponseRedirect('/display_1')
else:
form = Gallery1_Form()
return render(request, 'display_1/formForGallery1.html', {'form': form})

Save the views.py file and reload the form in your browser.

Fill out the form and submit. The content will be uploaded to the database and you will be redirected to the display_1 index page showing the newly uploaded item along with any previously entered objects.

You can also check the new item within the admin panel.

To make the form easier to get to, add a link to the form in the navigation bar.

Open the templates/components/navigation.html file and below the Gallery 1 link add:

        <li
{% if 'g1_post' in request.path %}
class="nav-item active "
{% else %}
class="nav-item "
{% endif %}
>
<a class="nav-link" href="{% url 'g1_post' %}">Post</a>
</li>

In the next step, we will go over how you can add custom style to the form rendered by Django Forms.

--

--