Django Forms.py Email And Phone Validation

房东的猫 提交于 2020-01-23 07:59:45

问题


I have a contact form created with forms.py that I would like to add validation with regular expressions or any built in Django validators for the email and phone fields. The only files I am using are forms.py, views.py, and the html template (there is no models.py for this contact form). If the user enters an incorrect phone number or email, I want to show them a message saying their format is incorrect and that they need to correct their input. The form should not be able to be submitted until the user enters valid data.

Right now entering fake data into the form and then submitting it causes the form to not do anything (it goes to # in the url) but the user has no idea if the email sent or not.

What I have tried below:

Forms.py:

from django import forms
from django.core.validators import EmailValidator
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
​
class ContactForm(forms.Form):
    contact_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off'}), required=True)
    contact_email = forms.EmailField(error_messages={'invalid': 'This is my email error msg.'}, widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off'}), required=True)
    contact_subject = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off'}), required=True)
    contact_phone = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off'}), required=True)
    content = forms.CharField(
    required=True,
    widget=forms.Textarea(attrs={'class':'form-control', 'autocomplete':'off'})
    )
​
    # the new bit we're adding
    def __init__(self, *args, **kwargs):
        super(ContactForm, self).__init__(*args, **kwargs)
        self.fields['contact_name'].label = "Full Name:"
        self.fields['contact_email'].label = "Email:"
        self.fields['contact_subject'].label = "Subject:"
        self.fields['contact_phone'].label = "Phone:"
        self.fields['content'].label = "Message:"
​
    def clean_email(self):
        email = self.cleaned_data['contact_email']
        validator = RegexValidator("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
        validator(email)
        return email

Views.py:

def contact(request):
    form_class = ContactForm
​
    # new logic!
    if request.method == 'POST':
        form = form_class(data=request.POST)
​
        if form.is_valid():
​
            recaptcha_response = request.POST.get('g-recaptcha-response')
            url = 'https://www.google.com/recaptcha/api/siteverify'
            payload = {
                'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
                'response': recaptcha_response
            }
            data = urllib.parse.urlencode(payload).encode()
            req = urllib.request.Request(url, data=data)

            response = urllib.request.urlopen(req)
            result = json.loads(response.read().decode())

            print('result:' + str(result))
​
            print('recaptcha_response:' + str(recaptcha_response))
​
            print('result_success:' + str(result['success']))

​
​
            if (not result['success']) or (not result['action'] == 'contact'):
                messages.error(request, 'Invalid reCAPTCHA. Please try again.')
​
            contact_name = request.POST.get(
                'contact_name'
            , '')
            contact_subject = request.POST.get(
                'contact_subject'
            , '')
            contact_email = request.POST.get(
                'contact_email'
            , '')
            contact_phone = request.POST.get(
                'contact_phone'
            , '')
            form_content = request.POST.get('content', '')
​
            # Email the profile with the
            # contact information
            template = get_template('contact_template.txt')
            context = {
                'contact_name': contact_name,
                'contact_email': contact_email,
                'contact_subject': contact_subject,
                'contact_phone': contact_phone,
                'form_content': form_content,
            }
            content = template.render(context)
​
            email = EmailMessage(
                contact_subject,
                content,
                "Your website" +'',
                ['email@gmail.com'],
                headers = {'Reply-To': contact_email }
            )
            email.send()
            messages.info(request, "Your message was sent successfully. Thank you for reaching out.")
​
    return render(request, 'contact.html', {
        'form': form_class,
    })

Html template:

<form id='cform' action="#" method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <script src='https://www.google.com/recaptcha/api.js?render=<KEY>'></script>
    <div class="g-recaptcha" data-sitekey="<KEY>"></div>
    <script>
        grecaptcha.ready(function() {
            grecaptcha.execute('<KEY>', {action: 'contact'})
            .then(function(token) {
                ginput = document.createElement('input');
                ginput.type = "hidden";
                ginput.name = "g-recaptcha-response";
                ginput.value = token;
                document.getElementById("cform").appendChild(ginput);
            });
        });
    </script>
    <button type="submit" class="btn btn-primary form-control">Submit</button>
</form>

How would I do this using Django only and without JavaScript?


回答1:


You can use EmailValidator instead of RegexValidator. That way you don't need to write a regex that can reliably test email addresses (which is hard to get right).

And to validate a phone number I would use the phonenumbers library. https://github.com/daviddrysdale/python-phonenumbers

Update

The Djano forms documentation is very good. The answers are here https://docs.djangoproject.com/en/3.0/ref/forms/

I'll give you some pointers.

The example for form handling with class-based views is a contact form.

https://docs.djangoproject.com/en/3.0/topics/class-based-views/generic-editing/

and this reference is excellent:

https://ccbv.co.uk/projects/Django/2.2/django.views.generic.edit/FormView/

For the email field everything you are trying to do is handled by defualt. This is enough:

class ContactForm(forms.Form):
    contact_email = forms.EmailField()

For the phone number, you can write your own validator, referring to https://docs.djangoproject.com/en/3.0/ref/forms/validation/ and the phonenumbers documentation. Something like:

from django.core.exceptions import ValidationError
import phonenumbers

def validate_phone_number(value):
    z = phonenumbers.parse(value, None)
    if not phonenumbers.is_valid_number(z):
        raise ValidationError(
            _('%(value) is not a valid phone number'),
            params={'value': value},
        )

Then

contact_phone = models.CharField(validators=[validate_phone_number])



回答2:


A third-party package (here) will contain the phone number validation you seek.

pip install django-phonenumber-field
pip install phonenumbers
# forms.py
...
from phonenumber_field.formfields import PhoneNumberField
...

class ContactForm(forms.Form):
    ... 
    contact_email = forms.EmailField(
        error_messages={'invalid': 'This is my email error msg.'}, 
        widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off'}), 
        required=True)
    ...
    contact_phone = PhoneNumberField(required=True)
    ...

Note also the comment in the project's README:

Representation can be set by PHONENUMBER_DB_FORMAT variable in django settings module. This variable must be one of 'E164', 'INTERNATIONAL', 'NATIONAL' or 'RFC3966'. Recommended is one of the globally meaningful formats 'E164', 'INTERNATIONAL' or 'RFC3966'. 'NATIONAL' format require to set up PHONENUMBER_DEFAULT_REGION variable.

The object returned is a PhoneNumber instance, not a string. If strings are used to initialize it, e.g. via MyModel(phone_number='+41524204242') or form handling, it has to be a phone number with country code.

So, you can (presumably, I have not tested this) via these settings configure the form widget to validate phone numbers according to your particular standards. (See the configuration options in the underlying package, phonenumbers.



来源:https://stackoverflow.com/questions/59295679/django-forms-py-email-and-phone-validation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!