Using HTML form to alter database

Hi everyone !

I would like to alter my database using the following HTML form :

<form id="newProductForm" method="POST">

        <label for="product_name">Product Name : </label>
        <input type="text" name="product_name">

        <label for="product_description">Description : </label>
        <input type="text" name="product_description">

        <label for="product_price">Price : </label>
        <input type="text" name="product_price">

        <div id="newProductForm_controls">
            <input type="submit" value="Confirm">
            <input type="button" value="Cancel" onclick="cancel()">
        </div>

</form>

And here is my “Product” model :

class Product(models.Model):
    product_name = models.CharField(max_length=80)
    product_description = models.CharField(max_length=100)
    product_quantity_available = models.PositiveIntegerField(default=0)
    product_price = models.PositiveIntegerField(default=0)

How to proceed ?

Thank you in advance :slight_smile:

If you haven’t already done so, I strongly encourage you to work through the Django Tutorial. It’ll help you get started in your understanding of how Django works with forms (among other important topics).

From there, you’ll probably be interested in reading more about Forms to see more of the different features available.

Hi @KenWhitesell !

Thank you for your answer :smiley:

I indeed took the time to read the said tutorials. What I understood is that the best way to create a HTML form able to alter the database is to create a Django ModelForm (as the models.py already contains the fields).

Yet, I don’t know how to “organize” this Django form.

Let’s take a more simple example with a absic ToDo app :

models.py

from django.db import models

    class Task(models.Model):
        task_name = models.CharField(max_length=40, default="")
        task_description = models.CharField(max_length=140, default="")

        def __str__(self):
            return self.task_name 

forms.py

from django import forms
from django.forms import ModelForm
from .models import Task

class TaskForm(ModelForm):
    class Meta:
        model = Task
        fields = ["task_name","task_description"]

views.py

from django.shortcuts import render
from .forms import TaskForm
from .models import Task

def index(request):

    if(request.method=="POST"):
        form = TaskForm(request.POST)

        if form.is_valid():
            form.save()

    else:
        form = TaskForm()

    task_list = Task.objects.all()
    context = {
        "form":form,
        "task_list":task_list, 
        }

    return render(request, "tasks/index.html", context)

index.html

{% block page_content %}
    <h2>To Do</h2>

    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">

    <div id="tasks_area">
        {% for task in task_list %}

            <div id="task">
                <p>Name : {{ task.task_name }} </p>
                <p>Description : {{ task.task_description }} </p>

                <div id="task_controls">
                    <input type="button" value="Update">
                    <input type="button" value="Delete">
                </div>

            </div>

        {% endfor %}

    </div>

{% endblock %}

The issue is that I can’t really “organize” the HTML {{ form }} tags as I only refer to it as {{ form }}, how can I add a placeholder ? Breaklines ?

Thank you in advance for your help

Great! Now you can move on to Working with Form Templates, which includes the section on Rendering fields manually.

Hi @KenWhitesell,

I tried to follow the tutorials but when I use :

{% csrf_token %}
<form id="submit_area" action="" method="POST">
    <input id="{{ form.task_name.id_for_input }}" type="text" placeholder="Type in your new task">
    <input type="submit" value="Submit">
</form> 

I get a CSRF error…

What I wanted to do is to link the first input with {{ form.task_name.id_for_input }} to the task_name Form Field but it doesn’t work

I’m just curious here - have you tried searching the Django documentation for CSRF?

Generally speaking, it’s going to be a lot quicker for you if you search there first before making a post here. If you can’t find something, or don’t understand what you’re reading, we’re more than happy to help. But this shouldn’t be your first stop for generic errors that are documented elsewhere.

Ken

The problem is that {% csrf_token %} must go inside the form tag. That template tag will expand into an input tag with type of hidden. Because you have the template tag outside of the form tag, your page is not sending the token with the form submission and is rejected by Django because it can’t complete the CSRF verification.

Hi @mblayman !

Yes, indeed, thank you for your explanation :slight_smile:

It works now but I don’t know how to alter the database here :

<form id="submit_area" action="" method="POST">
    {% csrf_token %}
    <input id="{{ form.task_name.id_for_input }}" type="text" placeholder="Type in your new task">
    <input type="submit" value="Submit">
</form>

I don’t understand why (and trust me I’ve read the Django form doc) because I wrote the save method in my views.py file :

from django.shortcuts import render
from .forms import TaskForm
from .models import Task

# Create your views here.
def index(request):
if(request.method=="POST"):
    form = TaskForm(request.POST)
    if form.is_valid():
        form.save()
else:
    form = TaskForm()

task_list = Task.objects.all()
context = {
    "form":form,
    "task_list":task_list, 
    }
    
return render(request, "tasks/index.html", context)

So when I hit the submit button, I should normally have a new model entry in my db but nope…

Ok, you’re trying to render your data entry field manually rather than rendering the form you’re creating.
You’re creating your form as TaskForm, but you’re not rendering that form (or the individual form fields) in your template. Also, your view doesn’t provide any means of showing errors that may appear, so the errors generated by your form are hidden.

If you make your form block in your template look like this:

<form id="submit_area" action="" method="POST">
    {% csrf_token %}
    {{ form.task_name}}
    {{ form.task_description }}
    <input type="submit" value="Submit">
</form>

It should work.

(Specifically what was happening was that you weren’t rendering your form, so the form didn’t know how to bind the values being returned by the browser - and so both database fields were null. Your form was generating errors on null values trying to be saved in non-null fields. Even if you would have rendered the task_name correctly, you still would have had an error on the task_description field because it wasn’t in your form at all.)

Ken

Hi @KenWhitesell, thanks it works now !