Django III

Serie of articles about developing a Full Django Web Site.


FORMS

CREATE NEW PROJECT

antiax@debian:~/Desktop/Django_course/DJANGO_LEVEL_THREE$ source activate MyDjangoEnv
(MyDjangoEnv) antiax@debian:~/Desktop/Django_course/DJANGO_LEVEL_THREE$ django-admin startproject basicforms
(MyDjangoEnv) antiax@debian:~/Desktop/Django_course/DJANGO_LEVEL_THREE$

Django III 1

(MyDjangoEnv) antiax@debian:~/Desktop/Django_course/DJANGO_LEVEL_THREE/basicforms$ django-admin startapp basicapp
(MyDjangoEnv) antiax@debian:~/Desktop/Django_course/DJANGO_LEVEL_THREE/basicforms$

Django III 2
create basicforms/templates/basicapp directory

create index.html & form_page.html into this directory

index.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>HOME</title>
  </head>
  <body>
    <h1>Welcome to the home page!</h1>
  </body>
</html>

form_page.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Forms</title>
  </head>
  <body>

  </body>
</html>

basicforms/basicforms/setting.py

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')

.....

INSTALLED_APPS = [
    ...
    'basicapp',
]

......

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [TEMPLATE_DIR,],

        ....

CREATE THE FORMS

create the file basicforms/basicapp/forms.py

from django import forms

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    text = forms.CharField( widget = forms.Textarea )

CREATE THE VIEWS

open the file basicforms/basicapp/views.py

from django.shortcuts import render
from . import forms

# Create your views here.

def index(request):
    return render(request, 'basicapp/index.html')

def form_name_view(request):
    form = forms.FormName()
    return render(request, 'basicapp/form_page.html', {'form': form})

MAPPING THE VIEWS

open project urls.py ( basicforms/basicforms/urls.py)

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

urlpatterns = [
    path('', views.index, name='index'),
    path('formpage/', views.form_name_view, name='form_name'),
    path('admin/', admin.site.urls),
]

( we have used the option: Function views )

INJECT FORMS

basicforms/templates/basicapp/form_page.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Forms</title>
  </head>
  <body>
    <div class="container">
      {{ form }}
    </div>
  </body>
</html>

TEST

(MyDjangoEnv) antiax@debian:~/Desktop/Django_course/DJANGO_LEVEL_THREE/basicforms$ python manage.py runserver

browse to http://127.0.0.1:8000/ and http://127.0.0.1:8000/formpage

TEMPLATE NICER

basicforms/templates/basicapp/form_page.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Forms</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>
    <div class="container">
      <h1>Fill out the form!</h1>
      <form method="POST">
        {{ form.as_p }}
        {% csrf_token %}
        <input type="submit" class="btn btn-primary" value="Submit">
      </form>
    </div>
  </body>
</html>

DO SOMETHING WITH THE DATA

open the file basicforms/basicapp/views.py

from django.shortcuts import render
from . import forms

# Create your views here.

def index(request):
    return render(request, 'basicapp/index.html')

def form_name_view(request):
    form = forms.FormName()

    if request.method == 'POST':
        form = forms.FormName(request.POST)

        if form.is_valid():
            # DO SOMETHING CODE
            print("VALIDATION SUCCESS!")
            print("NAME: " + form.cleaned_data['name'])
            print("EMAIL: " + form.cleaned_data['email'])
            print("TEXT: " + form.cleaned_data['text'])

    return render(request, 'basicapp/form_page.html', {'form': form})

the output on the console would be:

VALIDATION SUCCESS!
NAME: ananna
EMAIL: asaa@gmail.com
TEXT: adafadfdasf
[11/Jun/2019 16:52:53] "POST /formpage/ HTTP/1.1" 200 1043

FORM VALIDATION

First example, hidden fields and how we can use them for custom field validation that help us to detect potential “bots” (robots). The idea is that bots fill hidden fields which user would not never do.

ADD A HIDDEN INPUT TO THE FORM

basicforms/basicapp/forms.py

from django import forms

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    text = forms.CharField( widget = forms.Textarea )
    botcatcher = forms.CharField(required=False, widget=forms.HiddenInput)

Basic method VS Django Build-in validation

BASIC METHOD

Validation method clean_{field name} inside the Class

from django import forms

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    text = forms.CharField( widget = forms.Textarea )
    botcatcher = forms.CharField(required=False, widget=forms.HiddenInput)

    def clean_botcatcher(self):
        # get the value of the form
        botcatcher = self.cleaned_data['botcatcher']
        # validate it
        if len(botcatcher) &amp;amp;amp;amp;amp;amp;amp;gt; 0:
            raise forms.ValidationError("GOTCHA BOT!")
        return botcatcher

in the form page left bottom inspect and manually add field to hiddeninput value=”sneaky”
when “Submit” we get the error!

DJANGO BUILD-IN VALIDATORS

from django import forms
from django.core import validators

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    text = forms.CharField( widget = forms.Textarea )
    botcatcher = forms.CharField(required=False, widget=forms.HiddenInput,
                                validators=[validators.MaxLengthValidator(0)])

in the form page left bottom inspect and manually add field to hiddeninput value=”fooled”
when “Submit” we get the error!

ANOTHER EXAMPLE

Name field has to start with the letter ‘z’

Custom validation function using Django Build-in validators (NOT clean_ functions)

from django import forms
from django.core import validators

def check_for_z(value):
    if value[0].lower() != 'z':
        raise forms.ValidationError("NAME NEEDS TO START WITH Z")

class FormName(forms.Form):
    name = forms.CharField(validators=[check_for_z])
    email = forms.EmailField()
    text = forms.CharField( widget = forms.Textarea )
    botcatcher = forms.CharField(required=False, widget=forms.HiddenInput,
                                validators=[validators.MaxLengthValidator(0)])

LAST EXAMPLE

Enter twice the email, and both have to be identical.

Cleaning the entire form all at once! Custom clean method not for a particular field, instead for all form.
Remember clean_ method grab the form and let you to do something with the form’s data after the GOT call.

from django import forms
from django.core import validators

class FormName(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    verify_email = forms.EmailField(label='Enter your email again:')
    text = forms.CharField( widget = forms.Textarea )

    def clean(self):
        # return all data from the form after GOT
        all_clean_data = super().clean()
        # grab the fields we need
        email = all_clean_data['email']
        vmail = all_clean_data['verify_email']

        if email != vmail:
            raise forms.ValidationError("MAKE SURE EMAILS MATCH")

so custom validation function clean_{field name} grab particular form field
clean() grab entire form

Here we can download this project.

REVIEW: how works mapping

first: get urls.py from the project (project/project/urls.py)

path('', views.index, name="index"),
path('users/', include('AppTwo.urls')),

if we are using include get urls.py from the app (project/app/urls.py)

path('', views.users, name = "users"),

http://127.0.0.1:8000 —-> view index function —-> index.html
http://127.0.0.1:8000/users —–> view users function —–> users.html

MODEL FORMS

FORM to MODEL

Instead forms.Forms we will use forms.ModelForm in our forms.py file
documentation: Model Form Docs

generic code example:

from django import forms
from AppTwo.models import User

class FormUser(forms.ModelForm):
    # Form Fields go here
    class Meta:
        model = User
        fields = # Let's see the options.

Very common (not provide any additional field information) Modal fields = Form fields:

from django import forms
from AppTwo.models import User

class FormUser(forms.ModelForm):
    # Form Fields go here
    class Meta:
        model = User
        fields = # Let's see the options.

custom validators:

from django import forms
from AppTwo.models import User

class FormUser(forms.ModelForm):
    # Form Fields go here with validators params
    class Meta:
        model = User
        fields = # Let's see the options.

Options:

  1. fields = “__all__” —-> grab all the fields from model
  2. exclude = [“field1”, “field2”] —-> specify what fields want to exclude
  3. fields = (“field1”, “field2”) —–> list the fields included

EXERCISE

MAKE A HTML FILE WITH A FORM AND GET DATAS, THEN SAVE TO DATABASE CONNECTED TO THE
MODEL

STEPS:

  1. CRATE A MODELFORM IN FORMS.PY
  2. CONNECT THE FORM IN THE TEMPLATE
  3. EDIT VIEWS.PY TO SHOW THE FORM
  4. FIGURE OUT HOW TO .SAVE() THE DATA
  5. VERIFY THE MODEL IS ADMIN REGISTERED (OR SHOW IN BROWER)

models.py

from django.db import models

# Create your models here.

class User(models.Model):
    first_name = models.CharField(max_length = 264)
    last_name = models.CharField(max_length = 264)
    email = models.EmailField(unique = True)

    def __str__(self):
        return self.last_name + ", " + self.first_name

forms.py

from django import forms
from AppTwo.models import User

class FormUser(forms.ModelForm):
    class Meta:
        model = User
        fields = "__all__"

views.py
CHECK: how to save the model with the new element: form.save(commit=True)
CHECK: how to change the .html showed on the browser: return users(request)

from django.shortcuts import render
from django.http import HttpResponse
from . import forms
from AppTwo.models import User

# Create your views here.
def index(request):
    form = forms.FormUser()

    if request.method == 'POST':
        form = forms.FormUser(request.POST)

        if form.is_valid():
            form.save(commit=True)
            return users(request)
        else:
            print('ERROR FORM INVALID')

    return render(request, 'AppTwo/index.html', {'form': form})

def users(request):
    users_list = User.objects.order_by('last_name')
    users_dict = {'users': users_list}
    return render(request, 'AppTwo/users.html', context=users_dict)

We have worked on the project ProTwo from previous articles. We can download the full project.