Evgenii Legotckoi
15 октября 2016 г. 19:50

Django - Урок 013. Контактная форма на Django

Продолжая разработку сайта, хочу поделиться примером кода по добавлению контактной формы на сайте на Django . Уже были статьи с различными формами, например, для добавления комментариев, но раз говорим обо всём процессе в целом, то и не будем обходить и эту тему стороной.

Тем более, что для сайта на Wordpress это было для меня больной темой. Наверное, всему виной была лень, ибо у меня так и не возникло желания начать разбираться с PHP , чтобы набросать контактную форму самостоятельно (в итоге был задействован очередной плагин).

А если учесть, что разработка на Django , предполагает достаточно частую работу с различными формами данных и при этом имеется модуль для работы с почтовыми сервисами, то и добавление подобной формы не представляет особого труда.


Настройка settings.py, urls.py, home/urls.py

Первое, что сделаем, так это настроим конфигурационный файл, поскольку в нём необходимо будет указать данные для подключения к почтовому ящику с которого будет отправляться письмо с содержанием из контактной формы.

  1. EMAIL_HOST = 'smtp.example.com' # Сервер для отправки сообщений
  2. EMAIL_HOST_USER = 'info@example.com' # имя пользователя
  3. EMAIL_HOST_PASSWORD = 'password123' # пароль от ящика
  4. EMAIL_PORT = 2525 # порт для подключения
  5. EMAIL_USE_TLS = True # использование протокола шифрования
  6. DEFAULT_FROM_EMAIL = 'info@example.com' # email, с которого будет отправлено письмо

Также в данном файле необходимо указать то приложение, которое будет отвечать за контактную форму. В моём случае это приложение home, которое отвечает за редко меняющиеся страницы, например, страницу с контактной формой. А также используемый на сайте модуль django-bootstrap3

  1. INSTALLED_APPS = [
  2. ...
  3. 'home.apps.HomeConfig',
  4. 'bootstrap3',
  5. ...
  6. ]

Естественно в основном файле urls.py указан шаблон, по которому в данное приложение отправляется запрос.

  1. urlpatterns = [
  2. url(r'^', include('home.urls')),
  3. ]

Что касается шаблона url для приложения home , то он будет выглядеть следующим образом:

  1. from django.conf.urls import url
  2.  
  3. from . import views
  4.  
  5. app_name = 'home'
  6. urlpatterns = [
  7. ...
  8. url(r'^contacts/$', views.EContactsView.as_view(), name='contacts'),
  9. ...
  10. ]

Контактная форма

В контактной форме присутствует три поля:

  1. Имя - пользователь обязательно должен представиться;
  2. email - пользователь должен указать свою электронную почту, чтобы была возможность ему ответить;
  3. Сообщение - непосредственно само сообщение, которое пользователь желает отправить.

Все поля будут обязательными. При этом проверка корректности ввода email будет осуществляться ещё в браузере пользователя.

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django import forms
  4.  
  5.  
  6. class ContactForm(forms.Form):
  7.  
  8. name = forms.CharField(
  9. label="Имя",
  10. widget=forms.TextInput
  11. )
  12.  
  13. email = forms.EmailField(
  14. widget=forms.EmailInput
  15. )
  16.  
  17. message = forms.CharField(
  18. label="Сообщение",
  19. widget=forms.Textarea
  20. )

Представление

А теперь напишем представление, которое будет отвечать за обработку сообщения и отображение страницы с контактной формой.

  1. from django.shortcuts import render_to_response, reverse
  2. from django.views import View
  3. from django.core.mail import send_mail
  4.  
  5. from .forms import ContactForm
  6. from project import settings
  7.  
  8.  
  9. class EContactsView(View):
  10. template_name = 'home/contacts.html'
  11.  
  12. # В случае get запроса, мы будем отправлять просто страницу с контактной формой
  13. def get(self, request, *args, **kwargs):
  14. context = {}
  15. context.update(csrf(request)) # Обязательно добавьте в шаблон защитный токен
  16. context['contact_form'] = ContactForm()
  17.  
  18. return render_to_response(template_name=self.template_name, context=context)
  19.  
  20. def post(self, request, *args, **kwargs):
  21. context = {}
  22.  
  23. form = ContactForm(request.POST)
  24.  
  25. # Если не выполнить проверку на правильность ввода данных,
  26. # то не сможем забрать эти данные из формы... хотя что здесь проверять?
  27. if form.is_valid():
  28. email_subject = 'EVILEG :: Сообщение через контактную форму '
  29. email_body = "С сайта отправлено новое сообщение\n\n" \
  30. "Имя отправителя: %s \n" \
  31. "E-mail отправителя: %s \n\n" \
  32. "Сообщение: \n" \
  33. "%s " % \
  34. (form.cleaned_data['name'], form.cleaned_data['email'], form.cleaned_data['message'])
  35.  
  36. # и отправляем сообщение
  37. send_mail(email_subject, email_body, settings.EMAIL_HOST_USER, ['target_email@example.com'], fail_silently=False)
  38.  
  39. return render_to_response(template_name=self.template_name, context=context)

Здесь имеется один ключевой момент, касающийся отображения страницы для пользователя. Если пользователь отправил письмо, то ему необходимо сообщить, что письмо было отправлено. Для этого мы просто не будем помещать в контекст контактную форму и в шаблоне сделаем проверку на её наличие, чтобы отобразить корректную информацию пользователю.

Шаблон контактной формы

В контактной форме обязательно необходимо указать {% csrf_token %}, который будет защищать ваш сайт от атак через контактную форму. А также не забудьте подгрузить модуль bootsrtap3 , который позволит создать более корректный и красивый вид страницы.

Для использования контактной формы с модулем bootstrap3 необходимо всего лишь указать соответствующий шаблонный тег и передать в него нашу контактную форму. Обращаю ваше внимание также на то, что в зависимости от наличия контактной формы будет выведен различный внешний вид страницы.

  1. {% extends 'home/base.html' %}{% load bootstrap3 %}
  2. {% block title %}Контакты{% endblock %}
  3. {% block page %}
  4. <h1>Контакты</h1>
  5. <article>
  6. {% if contact_form %}
  7. <p>Добро пожаловать на сайт.</p>
  8. <p>Если у Вас есть пожелания или предложения по улучшению сайта, либо Вы желаете предложить статью к публикации на сайте, то Вы можете сделать это, воспользовавшись контактной формой:</p>
  9. <form id="contact_form" action="{% url 'home:contacts' %}" method="post">
  10. {% csrf_token %}
  11. {% bootstrap_form contact_form %}
  12. {% buttons %}
  13. <button type="submit" class="btn btn-primary">{% bootstrap_icon "send" %}&nbsp;&nbsp;Отправить</button>
  14. {% endbuttons %}
  15. </form>
  16. {% else %}
  17. <p>Сообщение отправлено</p>
  18. {% endif %}
  19. </article>
  20. {% endblock %}

Для Django рекомендую VDS-сервера хостера Timeweb .

Код контактной формы будет располагаться в файле forms.py.

Вам это нравится? Поделитесь в социальных сетях!

DR
  • 31 мая 2019 г. 21:12
  • (ред.)

Не полноценное решение, сильно вырвано из контекста.

B
  • 26 декабря 2019 г. 12:08

Здавствуйте! обязательно ли форма в django должна быть привязана к модели? конкретно в этом случаи

Evgenii Legotckoi
  • 6 января 2020 г. 14:16

Добрый день. Конкретно в этом случае модель не привязана к форме вообще потому, что здесь модель не используется. Вы можете использовать модель, чтобы дополнительно сохранять сообщения в базе данных сайта.

S
  • 17 мая 2020 г. 16:37

Добрый день,
не могу отправить загруженный файл, подскажите что нужно еще добавить.
Спасибо.

  1. forms:
  2. class ContactForm(forms.Form):
  3.  
  4. name = forms.CharField(
  5. label="Имя",
  6. widget=forms.TextInput
  7. )
  8.  
  9. email = forms.EmailField(
  10. widget=forms.EmailInput
  11. )
  12.  
  13. message = forms.CharField(
  14. label="Сообщение", required=False,
  15. widget=forms.Textarea
  16. )
  17. file = forms.FileField(
  18. label="Загрузить файл", required=False,
  19. )
  20.  
  21. views:
  22. class EContactsView(View):
  23. template_name = 'main/contacts.html'
  24.  
  25. # В случае get запроса, мы будем отправлять просто страницу с контактной формой
  26. def get(self, request, *args, **kwargs):
  27. context = {}
  28. context['contact_form'] = ContactForm()
  29. return render(request, template_name=self.template_name, context=context)
  30.  
  31. def post(self, request, *args, **kwargs):
  32. context = {}
  33.  
  34. form = ContactForm(request.POST, request.FILES)
  35.  
  36. # Если не выполнить проверку на правильность ввода данных,
  37. # то не сможем забрать эти данные из формы... хотя что здесь проверять?
  38. if form.is_valid():
  39.  
  40. email_subject = ':: Сообщение через контактную форму '
  41. email_body = "С сайта отправлено новое сообщение\n\n" \
  42. "Имя отправителя: %s \n" \
  43. "E-mail отправителя: %s \n\n" \
  44. "Сообщение: \n" \
  45. "%s " % \
  46. (form.cleaned_data['name'], form.cleaned_data['email'], form.cleaned_data['message'], form.cleaned_data['file'])
  47.  
  48.  
  49. # и отправляем сообщение
  50.  
  51. send_mail(email_subject, email_body, settings.EMAIL_HOST_USER, ['mymail@gmail.com'], fail_silently=False)
  52.  
  53. return render(request, template_name=self.template_name, context=context)
  54.  
  1. шаблон:
  2.  
  3. {% extends "layout/basic.html" %}
  4. {% load thumbnail %}
  5. {% load static %}
  6. {% load bootstrap4 %}
  7. {% block title %}Контакты{% endblock %}
  8.  
  9. {% block content %}
  10. <h1>Контакты</h1>
  11. <article>
  12. {% if contact_form %}
  13. <p>Добро пожаловать на сайт.</p>
  14. <p>Если у Вас есть пожелания или предложения по улучшению сайта, либо Вы желаете предложить статью к публикации на сайте, то Вы можете сделать это, воспользовавшись контактной формой:</p>
  15. <form id="contact_form" action="{% url 'main:contacts' %}" method="post" enctype="multipart/form-data">
  16. {% csrf_token %}
  17. {% bootstrap_form contact_form %}
  18.  
  19. {% buttons %}
  20. <button type="submit" class="btn btn-success">&nbsp;&nbsp;Отправить</button>
  21. {% endbuttons %}
  22. </form>
  23. {% else %}
  24. <p>Сообщение отправлено</p>
  25. {% endif %}
  26. </article>
  27. {% endblock %}
Evgenii Legotckoi
  • 18 мая 2020 г. 13:20

Добрый день.
Потому, что нужно не добавлять ссылку (или что там у вас в итоге получается) на файл и прикреплять его к письму.
Если не ошибаюсь, то для отправки письма с файлом нужно использовать класс EmailMessage

  1. def send_email(request):
  2. ...
  3. email = EmailMessage(
  4. subject,
  5. content,
  6. contact_email,
  7. [to],
  8. headers={'Reply-To': contact_email}
  9. )
  10. if request.FILES:
  11. uploaded_file = request.FILES['file'] # file is the name value which you have provided in form for file field
  12. email.attach(uploaded_file.name, uploaded_file.read(), uploaded_file.content_type)
  13. email.send()

Могут быть некоторые разночтения в синтаксисе, в зависимости от версий Django

S
  • 20 мая 2020 г. 17:08

Добрый день, в итоге получилось как-то так, отправляет форму с файлом.
Спасибо.

  1. settings:
  2.  
  3. EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
  4.  
  5. views:
  6.  
  7. from django.core.mail import EmailMessage
  8. from django.conf import settings
  9.  
  10. def contacts(request):
  11. if request.method == 'POST':
  12. form = ContactForm(request.POST)
  13. if form.is_valid():
  14. email_subject = ' Сообщение через контактную форму '
  15. email_body = "С сайта отправлено новое сообщение\n\n" \
  16. "Имя отправителя: %s \n" \
  17. "E-mail отправителя: %s \n\n" \
  18. "Сообщение: \n" \
  19. "%s " % \
  20. (form.cleaned_data['name'], form.cleaned_data['email'], form.cleaned_data['message'])
  21.  
  22. email = EmailMessage(
  23. email_subject,
  24. email_body,
  25. settings.EMAIL_HOST_USER,
  26. ['mymail@gmail.com'],
  27. )
  28. if request.FILES:
  29. uploaded_file = request.FILES['file'] # file is the name value which you have provided in form for file field
  30. email.attach(uploaded_file.name, uploaded_file.read(), uploaded_file.content_type)
  31. email.fail_silently=False
  32.  
  33. email.send()
  34. return render(request, 'main/contacts.html')
S
  • 22 мая 2020 г. 17:52

Добрый день,
при отправки письма с файлом, выбираю несколько файлов, пишет выбранное количество файлов, а отправляет только один, весь код см.выше, добавил только в форму возможность выбора нескольких файлов. Подскажите,что нужно еще добавить, чтобы можно было отправить сразу несколько файлов.
Спасибо.

  1. class ContactForm(forms.Form):
  2. file = forms.FileField(
  3. label="Загрузить файл", required=False,
  4. widget = forms.FileInput(attrs={'multiple': True})
  5.  
  6. )
Evgenii Legotckoi
  • 22 мая 2020 г. 17:57

Думаю, что как-то так, возможно неправильно написание имени метода getlist, но выглядеть должно так

  1. if request.FILES:
  2. for f in request.FILES.getlist('file'):
  3. email.attach(f.name, f.read(), f.content_type)
S
  • 22 мая 2020 г. 21:10

Спасибо, Евгений.
Все заработало.

  1. def contacts(request):
  2. if request.method == 'POST':
  3. form = ContactForm(request.POST)
  4. if form.is_valid():
  5. email_subject = ' Сообщение через контактную форму '
  6. email_body = "С сайта отправлено новое сообщение\n\n" \
  7. "Имя отправителя: %s \n" \
  8. "E-mail отправителя: %s \n\n" \
  9. "Сообщение: \n" \
  10. "%s " % \
  11. (form.cleaned_data['name'], form.cleaned_data['email'], form.cleaned_data['message'])
  12.  
  13. email = EmailMessage(
  14. email_subject,
  15. email_body,
  16. settings.EMAIL_HOST_USER,
  17. ['mymail@gmail.com'],
  18. )
  19. if request.FILES:
  20. for f in request.FILES.getlist('file'):
  21. email.attach(f.name, f.read(), f.content_type)
  22. email.fail_silently=False
  23. email.send()
  24. return render(request, 'main/contacts.html')
d
  • 16 февраля 2023 г. 22:15

Timeweb так себе провайдер.
Поддержка не отвечает. И smtp не отправляет письма.
Все перепробовал ничего найти не могу.
1. Ошибка sock.connect(sa) TimeoutError: [Errno 110] Connection timed out
2. Ошибка OSError: [Errno 101] Network is unreachable

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь