Django PhoneField not working as expected.

I am trying to store phone numbers in django models and forms. Charfield and IntegerField have character problems. Integerfield will only accept values up to a certain point and charfield is a string. I don’t know of any way to validate char field. So I looked for alternatives. I came across Phone Field which is explained in this stack overflow article.

Instructions for phone field.

Did I make a mistake in setup? The error message at the bottom says

Exception Type: TemplateDoesNotExist at /quote/customer/create/
Exception Value: phone_field/phone_widget.html

The thing is I don’t have a phone_widget.html file.

I get the following traceback error:

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/quote/customer/create/

Django Version: 5.0.7
Python Version: 3.12.4
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'quote',
 'debug_toolbar']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']

Template loader postmortem
Django tried loading these templates, in this order:

Using engine django:
    * django.template.loaders.filesystem.Loader: E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\templates\phone_field\phone_widget.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\contrib\admin\templates\phone_field\phone_widget.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\contrib\auth\templates\phone_field\phone_widget.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: E:\programming\django\django_projects\django_insurance\django_insurance\quote\templates\phone_field\phone_widget.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\debug_toolbar\templates\phone_field\phone_widget.html (Source does not exist)


Template error:
In template E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\templates\django\forms\p.html, error at line 9
   phone_field/phone_widget.html
   1 : {{ errors }}
   2 : {% if errors and not fields %}
   3 :   <p>{% for field in hidden_fields %}{{ field }}{% endfor %}</p>
   4 : {% endif %}
   5 : {% for field, errors in fields %}
   6 :   {{ errors }}
   7 :   <p{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>
   8 :     {% if field.label %}{{ field.label_tag }}{% endif %}
   9 :      {{ field }} 
   10 :     {% if field.help_text %}
   11 :       <span class="helptext"{% if field.auto_id %} id="{{ field.auto_id }}_helptext"{% endif %}>{{ field.help_text|safe }}</span>
   12 :     {% endif %}
   13 :     {% if forloop.last %}
   14 :       {% for field in hidden_fields %}{{ field }}{% endfor %}
   15 :     {% endif %}
   16 :   </p>
   17 : {% endfor %}
   18 : {% if not fields and not errors %}
   19 :   {% for field in hidden_fields %}{{ field }}{% endfor %}


Traceback (most recent call last):
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 33, in get_template
    return Template(self.engine.get_template(template_name), self)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\engine.py", line 177, in get_template
    template, origin = self.find_template(template_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\engine.py", line 163, in find_template
    raise TemplateDoesNotExist(name, tried=tried)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The above exception (phone_field/phone_widget.html) was the direct cause of the following exception:
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 171, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\test\utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\defaulttags.py", line 242, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1065, in render
    return render_value_in_context(output, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1042, in render_value_in_context
    value = str(value)
            ^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\utils.py", line 79, in __str__
    return self.as_widget()
           ^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\boundfield.py", line 108, in as_widget
    return widget.render(
           
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\widgets.py", line 279, in render
    return self._render(self.template_name, context, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\widgets.py", line 284, in _render
    return mark_safe(renderer.render(template_name, context))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\renderers.py", line 28, in render
    template = self.get_template(template_name)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\renderers.py", line 34, in get_template
    return self.engine.get_template(template_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 35, in get_template
    reraise(exc, self)
    ^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 84, in reraise
    raise new from exc
    ^^^^^^^^^^^^^^^^^^

The above exception (phone_field/phone_widget.html) was the direct cause of the following exception:
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 171, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\test\utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\loader_tags.py", line 159, in render
    return compiled_parent._render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\test\utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\loader_tags.py", line 65, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 1059, in render
    output = self.filter_expression.resolve(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 710, in resolve
    obj = self.var.resolve(context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 842, in resolve
    value = self._resolve_lookup(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\base.py", line 909, in _resolve_lookup
    current = current()
              ^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\utils.py", line 87, in as_p
    return self.render(self.template_name_p)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\utils.py", line 55, in render
    return mark_safe(renderer.render(template, context))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\renderers.py", line 29, in render
    return template.render(context, request=request).strip()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 63, in render
    reraise(exc, self.backend)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 84, in reraise
    raise new from exc
    ^^^^^^^^^^^^^^^^^^

The above exception (phone_field/phone_widget.html) was the direct cause of the following exception:
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\core\handlers\base.py", line 220, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\response.py", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\response.py", line 92, in rendered_content
    return template.render(context, self._request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 63, in render
    reraise(exc, self.backend)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\template\backends\django.py", line 84, in reraise
    raise new from exc
    ^^^^^^^^^^^^^^^^^^

Exception Type: TemplateDoesNotExist at /quote/customer/create/
Exception Value: phone_field/phone_widget.html

models.py

from django.db import models
from quote import constants
from phone_field import PhoneField
from phonenumber_field.modelfields import PhoneNumberField

# makemigrations and migrate from manage.py
# Create your models here.

# Tie everything with Quote ID


# customer model
class Customer(models.Model):
    # columns would be the variables
    first_name = models.CharField(max_length=50)
    middle_name = models.CharField(max_length=50, blank=True)
    last_name = models.CharField(max_length=50)
    suffix = models.CharField(blank=True, max_length=10)

    apt_number = models.CharField(blank=True, max_length=10)
    date_of_birth = models.DateField()
    address = models.CharField(max_length=50)
    zip_code = models.CharField(default=00000, max_length=10)
    telephone_number = PhoneField(blank=True, help_text='Contact phone number')
    email_address = models.CharField(max_length=50)
    quote_id = models.CharField(max_length=11, blank=True)
    home_ownership_options = (("OWN", "Owns_Property"), ("RENT", "Rents_Property"))
    home_ownership = models.CharField(max_length=50, choices=home_ownership_options)


# credit
# vehicle model
#
class Vehicle(models.Model):
    Vehicle_Identification_Number = models.CharField(max_length=30)
    # Enumerate Technique The first item in the tuple is what will be listed in the database.
    # The second item in the tuple is what will display for the user.
    Usage_Type_Options = (
        ("PLEASURE", "Pleasure Vehicle"),
        ("WORK", "Work/School"),
        ("BUSINESS", "Business"),
        ("COMMERCIAL", "Commercial"),
    )
    # Note: Use for ride sharing ideally should be like a check box 
    Used_For_Ride_Sharing = models.CharField(max_length = 10, blank=True)
    Usage_Type = models.CharField(choices=Usage_Type_Options, max_length=11, blank=True)
     
    Annual_Mileage_Options = (
        ("0 - 3,999", "0 - 3,999"), 
        ("4,000 - 5,999","4,000 - 5,999"),
        ("6,000 - 7,999", "6,000 - 7,999"), 
        ("8,000 - 9,999", "8,000 - 9,999"), 
        ("10,000 - 11,999", "10,000 - 11,999"), 
        ("12,000 - 13,999", "12,000 - 13,999"), 
        ("14,000 - 15,999", "14,000 - 15,999"), 
        ("16,000 - 17,999", "16,000 - 17,999"), 
        ("18,000 - 19,999", "18,000 - 19,999"), 
        ("20,000 OR MORE", "20,000 or more"), 
    )
    
    Annual_Mileage = models.CharField(choices=Annual_Mileage_Options, max_length=15)
    Year = models.IntegerField()
    Make = models.CharField(max_length=30)
    quote_id = models.CharField(max_length=11, blank=True)
    Model = models.CharField(max_length=30)
    
    Ownership_Options = (
        ("FINANCE", "Finance"),
        ("OWN", "Own"),  
        ("LEASE", "Lease"), 
    )
    
    Vehicle_Ownership = models.CharField(choices=Ownership_Options, max_length=11)
    
    Vehicle_Ownership_Timeframe_Options = (
        ("LESS THAN 1 MONTH", "Less than 1 month"), 
        ("1 TO 6 MONTHS", "1 to 6 months"), 
        ("6 MONTHS TO 1 YEAR", "6 months to 1 year"), 
        ("1 YEAR TO 3 YEARS", "1 year to 3 years"), 
        ("3 YEARS TO 5 YEARS", "3 years to 5 years"), 
        ("5 YEARS OR MORE", "5 years or more"),
    )
    
    Vehicle_Ownership_Timeframe = models.CharField(max_length=15)


class Driver(models.Model):
    driver_first_name = models.CharField(max_length=50, null=True)
    driver_last_name = models.CharField(max_length=50, null=True)
    driver_relation = models.CharField(max_length=16, choices=constants.DRIVER_RELATION_OPTIONS, default="SELF", null=True)
    quote_id = models.CharField(max_length=11, blank=True)
    drivers_license_state = models.CharField(choices=constants.STATE_OPTIONS, max_length=30, null=True)
    drivers_license_number = models.CharField(max_length=30, null=True)
    drivers_license_status = models.CharField(choices=constants.DRIVERS_LICENSE_STATUS, max_length=50, null=True)
    gender = models.CharField(choices=constants.GENDER_OPTIONS, max_length=30)
    date_of_issuance = models.DateField()
    job_status = models.CharField(max_length=50, choices=constants.JOB_STATUS_OPTIONS)
    education = models.CharField(choices=constants.EDUCATION_OPTIONS, max_length=35)
    affilation_options = (("DFW_PYTHONEERS", "Dfw_Pythoneers"),
                          ("PY_TEXAS", "Py_Texas"),)
    affilation = affilation_options


# many to many relationship QUOTE to PRODUCT
# state of texas requirements (LIABLITY,PIP,)
class Product(models.Model):
    product_type_options = (
        ("LIABILITY", "Liability"),
        ("RECCOMENDED_COVERAGE", "Reccomended Coverage"),
        ("CUSTOM", "Custom"),
    )
    coverage_options = (("FULL_COVERAGE", "Full Coverage"),)


# adding products to customers
# if not defined in a table before than you must use quotes for the model not defined.
# since everything is already defined already we do not need to worry about that.
class Quote(models.Model):
    Customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    Vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
    Driver = models.ForeignKey(Driver, on_delete=models.CASCADE)
    Reference_Number = models.CharField(max_length=30)
    Price = models.DecimalField(max_digits=7, decimal_places=2)

# url for vehcile we want to build a vehicle or for this quote 

forms.py

from django import forms
from quote.models import Driver, Vehicle
from phonenumber_field.modelfields import PhoneNumberField
class Customer_Form(forms.Form):
    first_name = forms.CharField(
        max_length=20, label="What is your first name?", required=True
    )
    middle_name = forms.CharField(
        max_length=20, label="What is your middle name?"
    )
    last_name = forms.CharField(
        max_length=20, label="What is your last name?", required=True
    )
    suffix = forms.CharField(
        max_length=5, label="If you have a suffix in your name what is it?", required=False
    )
    address = forms.CharField( 
        max_length=50, label="What is your address?", required=True
    )
    zip_code = forms.IntegerField(label="What is your zip code?")
    # dashes between the number
    telephone_number = PhoneNumberField()
    email = forms.EmailField(
        max_length=50, required=False, label="What is your email address?"
    )
    date_of_birth = forms.DateField(required=True, label="What is your date of birth?")
    home_ownership = forms.CharField(label="Do you own or rent your home?")


class VehicleForm(forms.ModelForm):
    class Meta:
        model = Vehicle
        fields = [
            "Vehicle_Identification_Number",
            "Usage_Type",
            "Year",
            "Make",
            "Model",
        ]

    quote_id = forms.CharField(
        widget=forms.HiddenInput(),
        required=False
    )


class DriverForm(forms.ModelForm):
    class Meta:
        model = Driver
        fields = [
            "driver_first_name",
            "driver_last_name",
            "driver_relation",
            "drivers_license_number",
            "drivers_license_status",
            'drivers_license_state',
            "date_of_issuance",
            "job_status",
            "gender",
            "education",
        ]
        widgets = {
            'date_of_issuance': forms.widgets.DateInput(attrs={'type': 'date'})
        }

    quote_id = forms.CharField(
        widget=forms.HiddenInput(),
        required=False
    )

That library is seriously out-of-date. Its last release was more than 4 years ago, and cannot be assumed to be valid for current Django releases. (Internal notes within the code shows that it was written for Django 1.10 and for Python < 3.7)

To use that library, you’ll need to understand how it is deficient relative to Django 5 and then upgrade it accordingly.

Creating functions to validate form fields is covered at Form and field validation | Django documentation | Django

1 Like

thank you for your help as always ken.

Attempting to write a validator for my phone number field, here is what I have so far.

views.py

def validate_phone_numbers(input):
    telephone_numbers = [["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]]
    if input not in telephone_numbers:
        raise ValidationError("Please input a number from 0 to 9")

forms.py

    telephone_number = forms.CharField(
        validators=[validate_phone_numbers]
        )

traceback

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/quote/customer/create/

Django Version: 5.0.7
Python Version: 3.12.4
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'quote']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback (most recent call last):
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\edit.py", line 178, in get
    return super().get(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\edit.py", line 142, in get
    return self.render_to_response(self.get_context_data())
                                   ^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\django_insurance\quote\views.py", line 55, in get_context_data
    context = super().get_context_data(**kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\edit.py", line 72, in get_context_data
    kwargs["form"] = self.get_form()
                     ^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\edit.py", line 36, in get_form
    form_class = self.get_form_class()
                 ^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\views\generic\edit.py", line 108, in get_form_class
    return model_forms.modelform_factory(model, fields=self.fields)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\models.py", line 652, in modelform_factory
    return type(form)(class_name, (form,), form_class_attrs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\programming\django\django_projects\django_insurance\venv\Lib\site-packages\django\forms\models.py", line 332, in __new__
    raise FieldError(message)
    ^^^^^^^^^^^^^^^^^^^^^^^^^

Exception Type: FieldError at /quote/customer/create/
Exception Value: Unknown field(s) (telephone_number) specified for Customer

Is your code on GitHub up to date? (I don’t see the validation code in it.)

right you are, I have just now pushed my branch with the codde, andrews branch, up to github.

class Customer(models.Model):
    ...
    telephone_number = CharField(validators=[validate_phone])

You don’t define form fields in the model. You define form fields in the form. Your model field needs to be a standard model field.

Also, your validator needs to be fixed. This line has no meaning in the context of this validator:
if not all in telephone_numbers:

The way you’re doing this validator, you need to check each character of the input individually to ensure it’s in the telephone_numbers list. (Or, you could create a regular expression validator.)

1 Like

i was really struggling with the documentation because it does not specify which file to put it in. but i must assume that the authors felt the readers would understand where to put it. my fault on that.

in any case, thank you for your help I will work on the corrections now.

Trying to write a validate function now for phone numbers as sadly this is not a built in form validator for django.

After researching the Django Docs and using the link provided by @KenWhitesell I still have a question.

Link to the form validation

writing validators

model forms

So I went into forms.py and wrote a function for validating the phone numbers.

forms.py

def validate_phone_numbers(value):
    telephone_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    for i in input:
        if i in telephone_numbers:
            continue
        else:
            raise ValidationError("Please input a number from 0 to 9")

And went into my Customer_Form and assigned this function to phone_number

from django import forms 
from quote.models import Driver, Vehicle
from django.forms import CharField
from django.core.exceptions import ValidationError

from django import forms

from quote.models import Driver, Vehicle

from django.forms import CharField

from django.core.exceptions import ValidationError

def validate_phone_numbers(value):
telephone_numbers = [“0”, “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”]
for i in input:
if i in telephone_numbers:
continue
else:
raise ValidationError(“Please input a number from 0 to 9”)

class Customer_Form(forms.Form):

phone_number = forms.CharField(
    validators=[validate_phone_numbers], max_length=10, label="What is your phone number?"
    )

The problem I am experiencing is that when I launch the project and run the quoting software to the next page my website still accepts non valid values. Phone numbers cannot contain letters but the submission happens anwways. its as if my validator never activates. 

Where did I go wrong? Any advice?

I’m a little surprised you’re not getting an error in your validator.

You have:

def validate_phone_numbers(value):
    telephone_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    for i in input:
        if i in telephone_numbers:
            continue
        else:
            raise ValidationError("Please input a number from 0 to 9")

You are not checking your input data here.

This:

def validate_phone_numbers(value):
                           ^^^^^

Does not match:

    for i in input:
             ^^^^^

I foresee more issues with this type of validation in future depending on how you are storing the phones. There are different formats for storing phone numbers. It gets even more interesting when you have to consider country codes, character spacing and special characters like (+, or parenthesis). You may escape stress in the near future by storing the numbers as Characters ( CharField)

After implementing the suggested changes and attempting to fill out the form it seems the browser accepts the wrong telephone number information anyway. Its as if the validation error is never raised. If it is raised, I probably need to specify to django what to do. But its as if it is not called. Have I set this validator up incorrectly?

forms.py

from django import forms 
from quote.models import Driver, Vehicle
from django.forms import CharField
from django.core.exceptions import ValidationError

def validate_phone_numbers(value):
    telephone_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    for i in value:
        if i in telephone_numbers:
            continue
        else:
            raise ValidationError("Please input a number from 0 to 9")
forms.py continued 

class Customer_Form(forms.Form):

phone_number = forms.CharField(
    validators=[validate_phone_numbers], max_length=10, label="What is your phone number?"
    )


According to your validator, what you are showing here is valid. Why would you expect the validation error to be thrown?

views.py

# quote/views.py

from typing import Any
from django.shortcuts import redirect, render
from django.http import HttpRequest
from django.views.generic.edit import CreateView
from django.views.generic import TemplateView
from quote.forms import Customer_Form, DriverForm, VehicleForm
from quote import models
from django.views.generic.list import ListView
import random
from django.forms import CharField 

# need to import views in the urls.py of quote

# class RegexValidator(regex=None, message=None, code=None, inverse_match=None, flags=0)[source]


class Customer_CreateView(CreateView):
    quote_id: str = None
    model = models.Customer
    fields = [
        "first_name",
        "last_name",
        "address",
        "phone_number",
        "zip_code",
        "email_address",
        "date_of_birth",
        "home_ownership"
    ]
    template_name = "customer.html"

    def form_valid(self, form):
        form.instance.quote_id = self.quote_id
        return super().form_valid(form)

    def get_success_url(self):
        return f"/driver/list/{self.quote_id}"

    # we need to utilize the setup() function in createview. we need to add functionality
    # to setup to create a quote_id for django to know that this quote is for this customer.
    def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None:
        current_quote_id = request.COOKIES.get('quote_id', '')
        if current_quote_id:
            self.quote_id = current_quote_id
            print(f'setting up quote id: {self.quote_id}')
        else:
            self.quote_id = create_quote_id()
        return super().setup(request, *args, **kwargs)

    # each view has its own context
    # in order to change context in django we need the get_context function
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['quote_id'] = self.quote_id
        context['title'] = "Customer Page"
        return context

    def render_to_response(self, context, **response_kwargs):
        response = super().render_to_response(context, **response_kwargs)
        response.set_cookie('quote_id', self.quote_id, max_age=3600)
        return response


""" 
I want to create a function where the user clicks on the get quote button and that tells django to load the custoemr form to disaply
and for the user to fill out. The quote ID must come with it.

urls.py-> path("quote/customer/create/", Customer_CreateView.as_view(), name='customer'),
views.py needs to take in a request and a quote id 
template being used 

"""
def customer_form_view(request, quote_id):
    quote_id = request.COOKIES.get("quote_id")
    form = Customer_Form()
    render(
        request,
        "customer.html",
        {
            "form": form,
            "title": "Customer",
            "quote_id": quote_id,
        },
    )
    return render(request)


class HomePageView(TemplateView):
    template_name = "home.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = "Home Page"
        context['description'] = "We provide the best services."
        return context

class AboutPageView(TemplateView):
    template_name = "about.html"


class DriverListView(ListView):
    template_name = 'driver-list.html'
    model = models.Driver
    # object_list

    def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None:
        self.quote_id = kwargs['quote_id']
        print(f"Quote is: {self.quote_id}")
        return super().setup(request, *args, **kwargs)

    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
        context = super().get_context_data(**kwargs)
        context['quote_id'] = self.quote_id
        print(f"context is: {context}")
        driver_list = models.Driver.objects.filter(quote_id=self.quote_id)
        context['driver_list'] = driver_list
        print(f"context is: {context}")
        return context


def vehicle_form_view(request):
    quote_id = request.COOKIES.get('quote_id')
    # TODO handle post request to validate and save data.
    form = VehicleForm()
    return render(
        request,
        'vehicles.html',
        {
            'form': form,
            'title': 'Vehicle',
            'quote_id': quote_id,
        }
    )


def driver_form(request, quote_id):
    # TODO should not allow access to this page without a quote id
    # in order to maintain data integrity
    # quote_id = request.COOKIES.get('quote_id')
    print(f"Quote id is: {quote_id}")
    print(f"request method: {request}")

    if request.method == 'POST':
        form = DriverForm(request.POST)
        print(f'form valid? : {form.is_valid()}')
        if form.is_valid():
            driver = form.save(commit=False)
            driver.quote_id = quote_id
            driver.save()
            return redirect('f/driver/list{quote_id}')
           
    form = DriverForm()
    return render(
        request, 
        'driver.html',
        {
            'form': form,
            'title': 'Driver Page',
            'quote_id': quote_id,
        }
    )


def create_quote_id():
    result = "dj"
    for _ in range(6):
        result += str(random.randint(1, 10))
    return result

Im not ready for class based views. I think I am going to write function views and try to call my code.

Ok, I see the issue here.

In your CreateView, you’re defining the fields for the CreateView to create a form itself. You’re not using your Customer_Form in that view.

Instead of defining the fields, you should use the form_class = Customer_Form attribute in your CreateView to use that form instead of creating one itself.

Also, you might want to consider making Customer_Form a ModelForm and not just a regular form. (I don’t know if it’s necessary or not, but it may make some things easier.)

1 Like

Per Ken
Ok, I see the issue here.

In your CreateView, you’re defining the fields for the CreateView to create a form itself. You’re not using your Customer_Form in that view.

Instead of defining the fields, you should use the form_class = Customer_Form attribute in your CreateView to use that form instead of creating one itself.

Andrew: → Like this?

class Customer_CreateView(CreateView):
    quote_id: str = None
    model = models.Customer
    form_class = Customer_Form
    template_name = "customer.html"

    def form_valid(self, form):
        form.instance.quote_id = self.quote_id
        return super().form_valid(form)

    def get_success_url(self):
        return f"/driver/list/{self.quote_id}"

    # we need to utilize the setup() function in createview. we need to add functionality
    # to setup to create a quote_id for django to know that this quote is for this customer.

    def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None:
        current_quote_id = request.COOKIES.get("quote_id", "")
        if current_quote_id:
            self.quote_id = current_quote_id
            print(f"setting up quote id: {self.quote_id}")
        else:
            self.quote_id = create_quote_id()
        return super().setup(request, *args, **kwargs)

    # each view has its own context
    # in order to change context in django we need the get_context function
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["quote_id"] = self.quote_id
        context["title"] = "Customer Page"
        return context