[Python-modules-commits] [python-django-contact-form] 01/05: New upstream version 1.4.2
Andrew Starr-Bochicchio
asb at moszumanska.debian.org
Mon Jul 3 14:55:57 UTC 2017
This is an automated email from the git hooks/post-receive script.
asb pushed a commit to branch master
in repository python-django-contact-form.
commit 0bbefda541a91ffaf1dd423f0d7b5553992eb858
Author: Andrew Starr-Bochicchio <a.starr.b at gmail.com>
Date: Mon Jul 3 10:43:30 2017 -0400
New upstream version 1.4.2
---
.gitignore | 5 +-
.travis.yml | 21 +------
LICENSE | 2 +-
MANIFEST.in | 1 +
Makefile | 60 +++++++++++++++++++
README.rst | 4 +-
contact_form/{urls.py => akismet_urls.py} | 12 ++--
contact_form/forms.py | 83 ++++++++++++++++++++------
contact_form/runtests.py | 8 ++-
contact_form/tests/test_forms.py | 75 +++++++++++++++++++++++-
contact_form/tests/test_urls.py | 8 ++-
contact_form/tests/test_views.py | 53 ++++++++++++++++-
contact_form/urls.py | 4 +-
contact_form/views.py | 27 ++-------
docs/conf.py | 6 +-
docs/faq.rst | 67 ++++++++++++---------
docs/forms.rst | 97 +++++++++++++++++++++++++++----
docs/index.rst | 11 ++--
docs/install.rst | 60 ++++++++-----------
docs/quickstart.rst | 62 ++++++++++++++++----
docs/views.rst | 21 ++-----
setup.cfg | 4 ++
setup.py | 12 ++--
test_requirements.txt | 3 +
tox.ini | 46 +++++++++++++++
25 files changed, 566 insertions(+), 186 deletions(-)
diff --git a/.gitignore b/.gitignore
index 955f665..518cfaf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,7 @@ __pycache__
*.egg-info
docs/_build/
dist/
-.coverage
\ No newline at end of file
+.coverage
+.python-version
+.tox/
+*.sh
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 67dd0d4..b97d414 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,25 +1,10 @@
language: python
sudo: false
python:
+ - "3.6"
- "3.5"
- "3.4"
- "3.3"
- "2.7"
-env:
- - DJANGO_VERSION=1.8.16
- - DJANGO_VERSION=1.9.11
- - DJANGO_VERSION=1.10.3
-install:
- - pip install coverage>=4.0
- - pip install flake8
- - pip install -q Django==$DJANGO_VERSION
-matrix:
- exclude:
- - python: "3.3"
- env: DJANGO_VERSION=1.9.11
- - python: "3.3"
- env: DJANGO_VERSION=1.10.3
-script:
- - coverage run setup.py test
- - flake8 contact_form
- - coverage report -m
+install: pip install tox-travis
+script: tox
diff --git a/LICENSE b/LICENSE
index 5f67d07..ccf813a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2007-2016, James Bennett
+Copyright (c) 2007-2017, James Bennett
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/MANIFEST.in b/MANIFEST.in
index 93e2657..814c0bb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,5 @@
include LICENSE
include MANIFEST.in
include README.rst
+include Makefile
recursive-include docs *
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2b88f7e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+PACKAGE_NAME=contact_form
+TESTING_VENV_NAME=${PACKAGE_NAME}_test
+
+ifndef PYTHON_VERSION
+PYTHON_VERSION=3.6.0
+endif
+
+ifndef DJANGO_VERSION
+DJANGO_VERSION=1.10
+endif
+
+.PHONY: clean
+clean:
+ python setup.py clean
+ rm -rf build/
+ rm -rf dist/
+ rm -rf *.egg*/
+ rm -rf ${PACKAGE_NAME}/__pycache__/
+ rm -rf ${PACKAGE_NAME}/tests/__pycache__/
+ find ${PACKAGE_NAME} -type f -name '*.pyc' -delete
+ rm -f MANIFEST
+ rm -rf coverage .coverage .coverage*
+ pip uninstall -y Django
+
+.PHONY: venv
+venv:
+ [ -e ~/.pyenv/versions/${TESTING_VENV_NAME} ] && \
+ echo "\033[1;33mA ${TESTING_VENV_NAME} pyenv already exists. Skipping pyenv creation.\033[0m" || \
+ pyenv virtualenv ${PYTHON_VERSION} ${TESTING_VENV_NAME}
+ pyenv local ${TESTING_VENV_NAME}
+ pip install --upgrade pip setuptools
+
+.PHONY: teardown
+teardown: clean
+ pyenv uninstall -f ${TESTING_VENV_NAME}
+ rm .python-version
+
+.PHONY: django
+django:
+ pip install Django~=${DJANGO_VERSION}.0
+
+.PHONY: test_dependencies
+test_dependencies:
+ [ -e test_requirements.txt ] && \
+ pip install -r test_requirements.txt || \
+ echo "\033[1;33mNo test_requirements.txt found. Skipping install of test_requirements.txt.\033[0m"
+ pip install -r test_requirements.txt
+
+.PHONY: lint
+lint: test_dependencies
+ flake8 ${PACKAGE_NAME}
+
+.PHONY: test
+test: django lint
+ [ -e requirements.txt ] \
+ && pip install -r requirements.txt || \
+ echo "\033[1;33mNo requirements.txt found. Skipping install of requirements.txt.\033[0m"
+ pip install -e .
+ coverage run ${PACKAGE_NAME}/runtests.py
+ coverage report -m
diff --git a/README.rst b/README.rst
index e7f5cdb..f6c4684 100644
--- a/README.rst
+++ b/README.rst
@@ -3,8 +3,8 @@
.. image:: https://travis-ci.org/ubernostrum/django-contact-form.svg?branch=master
:target: https://travis-ci.org/ubernostrum/django-contact-form
-This application provides simple, extensible contact-form functionality
-for `Django <https://www.djangoproject.com/>`_ sites.
+This application provides extensible contact-form functionality for
+`Django <https://www.djangoproject.com/>`_ sites.
Full documentation for all functionality is included and is also
`available online <http://django-contact-form.readthedocs.io/>`_.
\ No newline at end of file
diff --git a/contact_form/urls.py b/contact_form/akismet_urls.py
similarity index 56%
copy from contact_form/urls.py
copy to contact_form/akismet_urls.py
index e925044..3906355 100644
--- a/contact_form/urls.py
+++ b/contact_form/akismet_urls.py
@@ -1,21 +1,23 @@
"""
-Example URLConf for a contact form.
+Example URLConf for a contact form with Akismet spam filtering.
-If all you want is the basic ContactForm with default behavior, just
+If all you want is the basic contact-form plus spam filtering,
include this URLConf somewhere in your URL hierarchy (for example, at
-``/contact/``)>
+``/contact/``).
"""
from django.conf.urls import url
from django.views.generic import TemplateView
-from contact_form.views import ContactFormView
+from .forms import AkismetContactForm
+from .views import ContactFormView
urlpatterns = [
url(r'^$',
- ContactFormView.as_view(),
+ ContactFormView.as_view(
+ form_class=AkismetContactForm),
name='contact_form'),
url(r'^sent/$',
TemplateView.as_view(
diff --git a/contact_form/forms.py b/contact_form/forms.py
index 4506859..d49bc02 100644
--- a/contact_form/forms.py
+++ b/contact_form/forms.py
@@ -3,14 +3,14 @@ A base contact form for allowing users to send email messages through
a web interface.
"""
+import os
from django import forms
from django.conf import settings
-from django.contrib.sites.models import Site
-from django.contrib.sites.requests import RequestSite
+from django.contrib.sites.shortcuts import get_current_site
from django.utils.translation import ugettext_lazy as _
from django.core.mail import send_mail
-from django.template import RequestContext, loader
+from django.template import loader
class ContactForm(forms.Form):
@@ -49,12 +49,12 @@ class ContactForm(forms.Form):
Render the body of the message to a string.
"""
- if callable(self.template_name):
- template_name = self.template_name()
- else:
- template_name = self.template_name
- return loader.render_to_string(template_name,
- self.get_context())
+ template_name = self.template_name() if \
+ callable(self.template_name) \
+ else self.template_name
+ return loader.render_to_string(
+ template_name, self.get_context(), request=self.request
+ )
def subject(self):
"""
@@ -64,8 +64,9 @@ class ContactForm(forms.Form):
template_name = self.subject_template_name() if \
callable(self.subject_template_name) \
else self.subject_template_name
- subject = loader.render_to_string(template_name,
- self.get_context())
+ subject = loader.render_to_string(
+ template_name, self.get_context(), request=self.request
+ )
return ''.join(subject.splitlines())
def get_context(self):
@@ -88,13 +89,7 @@ class ContactForm(forms.Form):
raise ValueError(
"Cannot generate Context from invalid contact form"
)
- if Site._meta.installed:
- site = Site.objects.get_current()
- else:
- site = RequestSite(self.request)
- return RequestContext(self.request,
- dict(self.cleaned_data,
- site=site))
+ return dict(self.cleaned_data, site=get_current_site(self.request))
def get_message_dict(self):
"""
@@ -130,3 +125,55 @@ class ContactForm(forms.Form):
"""
send_mail(fail_silently=fail_silently, **self.get_message_dict())
+
+
+class AkismetContactForm(ContactForm):
+ """
+ Contact form which doesn't add any extra fields, but does add an
+ Akismet spam check to the validation routine.
+
+ Requires the Python Akismet library, and two configuration
+ parameters: an Akismet API key and the URL the key is associated
+ with. These can be supplied either as the settings AKISMET_API_KEY
+ and AKISMET_BLOG_URL, or the environment variables
+ PYTHON_AKISMET_API_KEY and PYTHON_AKISMET_BLOG_URL.
+
+ """
+ SPAM_MESSAGE = _(u"Your message was classified as spam.")
+
+ def _is_unit_test(self):
+ """
+ Determine if we're in a test run.
+
+ During test runs, Akismet should be passed the ``is_test``
+ argument to ensure no effect on training corpus.
+
+ django-contact-form's tox.ini will set the environment
+ variable ``CI`` to indicate this, as will most online
+ continuous-integration systems.
+
+ """
+ return os.getenv('CI') == 'true'
+
+ def clean_body(self):
+ if 'body' in self.cleaned_data:
+ from akismet import Akismet
+ akismet_api = Akismet(
+ key=getattr(settings, 'AKISMET_API_KEY', None),
+ blog_url=getattr(settings, 'AKISMET_BLOG_URL', None)
+ )
+ akismet_kwargs = {
+ 'user_ip': self.request.META['REMOTE_ADDR'],
+ 'user_agent': self.request.META.get('HTTP_USER_AGENT'),
+ 'comment_author': self.cleaned_data.get('name'),
+ 'comment_author_email': self.cleaned_data.get('email'),
+ 'comment_content': self.cleaned_data['body'],
+ 'comment_type': 'contact-form',
+ }
+ if self._is_unit_test():
+ akismet_kwargs['is_test'] = 1
+ if akismet_api.comment_check(**akismet_kwargs):
+ raise forms.ValidationError(
+ self.SPAM_MESSAGE
+ )
+ return self.cleaned_data['body']
diff --git a/contact_form/runtests.py b/contact_form/runtests.py
index 549e2db..deccca3 100644
--- a/contact_form/runtests.py
+++ b/contact_form/runtests.py
@@ -26,7 +26,7 @@ SETTINGS_DICT = {
'django.contrib.contenttypes',
'django.contrib.sites',
),
- 'ROOT_URLCONF': 'contact_form.tests.urls',
+ 'ROOT_URLCONF': 'contact_form.tests.test_urls',
'DATABASES': {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
@@ -75,6 +75,10 @@ def run_tests():
TestRunner = get_runner(settings)
# And then we run tests and return the results.
- test_runner = TestRunner(verbosity=1, interactive=True)
+ test_runner = TestRunner(verbosity=2, interactive=True)
failures = test_runner.run_tests(['contact_form.tests'])
sys.exit(bool(failures))
+
+
+if __name__ == '__main__':
+ run_tests()
diff --git a/contact_form/tests/test_forms.py b/contact_form/tests/test_forms.py
index 0691163..d223396 100644
--- a/contact_form/tests/test_forms.py
+++ b/contact_form/tests/test_forms.py
@@ -1,11 +1,19 @@
+import os
+import unittest
+
from django.conf import settings
from django.core import mail
from django.test import RequestFactory, TestCase
+from django.utils.six import text_type
-from ..forms import ContactForm
+from ..forms import AkismetContactForm, ContactForm
class ContactFormTests(TestCase):
+ """
+ Tests the base ContactForm.
+
+ """
valid_data = {'name': 'Test',
'email': 'test at example.com',
'body': 'Test message'}
@@ -139,3 +147,68 @@ class ContactFormTests(TestCase):
self.assertEqual(overridden_data,
form.get_message_dict())
+
+
+ at unittest.skipUnless(
+ getattr(
+ settings,
+ 'AKISMET_API_KEY',
+ os.getenv('PYTHON_AKISMET_API_KEY')
+ ) is not None,
+ "AkismetContactForm requires Akismet configuration"
+)
+class AkismetContactFormTests(TestCase):
+ """
+ Tests the Akismet contact form.
+
+ """
+ def request(self):
+ return RequestFactory().request()
+
+ def test_akismet_form_test_detection(self):
+ """
+ The Akismet contact form correctly detects a test environment.
+
+ """
+ form = AkismetContactForm(request=self.request())
+ self.assertTrue(form._is_unit_test())
+ try:
+ old_environ = os.getenv('CI')
+ os.environ['CI'] = ''
+ form = AkismetContactForm(request=self.request())
+ self.assertFalse(form._is_unit_test())
+ finally:
+ if old_environ is not None:
+ os.environ['CI'] = old_environ
+
+ def test_akismet_form_spam(self):
+ """
+ The Akismet contact form correctly rejects spam.
+
+ """
+ data = {'name': 'viagra-test-123',
+ 'email': 'email at example.com',
+ 'body': 'This is spam.'}
+ form = AkismetContactForm(
+ request=self.request(),
+ data=data
+ )
+ self.assertFalse(form.is_valid())
+ self.assertTrue(
+ text_type(form.SPAM_MESSAGE) in
+ form.errors['body']
+ )
+
+ def test_akismet_form_ham(self):
+ """
+ The Akismet contact form correctly accepts non-spam.
+
+ """
+ data = {'name': 'Test',
+ 'email': 'email at example.com',
+ 'body': 'Test message.'}
+ form = AkismetContactForm(
+ request=self.request(),
+ data=data
+ )
+ self.assertTrue(form.is_valid())
diff --git a/contact_form/tests/test_urls.py b/contact_form/tests/test_urls.py
index 19b4289..2e6843c 100644
--- a/contact_form/tests/test_urls.py
+++ b/contact_form/tests/test_urls.py
@@ -6,6 +6,7 @@ URLConf for testing django-contact-form.
from django.conf.urls import url
from django.views.generic import TemplateView
+from ..forms import AkismetContactForm
from ..views import ContactFormView
@@ -16,10 +17,15 @@ urlpatterns = [
url(r'^sent/$',
TemplateView.as_view(
template_name='contact_form/contact_form_sent.html'
- ),
+ ),
name='contact_form_sent'),
url(r'^test_recipient_list/$',
ContactFormView.as_view(
recipient_list=['recipient_list at example.com']),
name='test_recipient_list'),
+ url(r'^test_akismet_form/$',
+ ContactFormView.as_view(
+ form_class=AkismetContactForm
+ ),
+ name='test_akismet_form'),
]
diff --git a/contact_form/tests/test_views.py b/contact_form/tests/test_views.py
index 9dee61f..1086400 100644
--- a/contact_form/tests/test_views.py
+++ b/contact_form/tests/test_views.py
@@ -1,11 +1,18 @@
+import os
+import unittest
+
from django.conf import settings
from django.core import mail
-from django.core.urlresolvers import reverse
from django.test import RequestFactory, TestCase
from django.test.utils import override_settings
from ..forms import ContactForm
+try:
+ from django.urls import reverse
+except ImportError: # pragma: no cover
+ from django.core.urlresolvers import reverse # pragma: no cover
+
@override_settings(ROOT_URLCONF='contact_form.tests.test_urls')
class ContactFormViewTests(TestCase):
@@ -87,3 +94,47 @@ class ContactFormViewTests(TestCase):
message = mail.outbox[0]
self.assertEqual(['recipient_list at example.com'],
message.recipients())
+
+
+ at unittest.skipUnless(
+ getattr(
+ settings,
+ 'AKISMET_API_KEY',
+ os.getenv('PYTHON_AKISMET_API_KEY')
+ ) is not None,
+ "AkismetContactForm requires Akismet configuration"
+)
+class AkismetContactFormViewTests(TestCase):
+ """
+ Tests the views with the Akismet contact form.
+
+ """
+ def test_akismet_view_spam(self):
+ """
+ The Akismet contact form errors on spam.
+
+ """
+ contact_url = reverse('test_akismet_form')
+ data = {'name': 'viagra-test-123',
+ 'email': 'email at example.com',
+ 'body': 'This is spam.'}
+ response = self.client.post(contact_url,
+ data=data)
+ self.assertEqual(200, response.status_code)
+ self.assertFalse(response.context['form'].is_valid())
+ self.assertTrue(response.context['form'].has_error('body'))
+
+ def test_akismet_view_ham(self):
+ contact_url = reverse('test_akismet_form')
+ data = {'name': 'Test',
+ 'email': 'email at example.com',
+ 'body': 'Test message.'}
+ response = self.client.post(contact_url,
+ data=data)
+ self.assertRedirects(response,
+ reverse('contact_form_sent'))
+ self.assertEqual(1, len(mail.outbox))
+
+ message = mail.outbox[0]
+ self.assertEqual(['noreply at example.com'],
+ message.recipients())
diff --git a/contact_form/urls.py b/contact_form/urls.py
index e925044..f5f0115 100644
--- a/contact_form/urls.py
+++ b/contact_form/urls.py
@@ -1,9 +1,9 @@
"""
Example URLConf for a contact form.
-If all you want is the basic ContactForm with default behavior, just
+If all you want is the basic ContactForm with default behavior,
include this URLConf somewhere in your URL hierarchy (for example, at
-``/contact/``)>
+``/contact/``)
"""
diff --git a/contact_form/views.py b/contact_form/views.py
index a06e967..8aa1d83 100644
--- a/contact_form/views.py
+++ b/contact_form/views.py
@@ -3,11 +3,15 @@ View which can render and send email from a contact form.
"""
-from django.core.urlresolvers import reverse
from django.views.generic.edit import FormView
from .forms import ContactForm
+try:
+ from django.urls import reverse
+except ImportError: # pragma: no cover
+ from django.core.urlresolvers import reverse # pragma: no cover
+
class ContactFormView(FormView):
form_class = ContactForm
@@ -18,27 +22,6 @@ class ContactFormView(FormView):
form.save()
return super(ContactFormView, self).form_valid(form)
- def form_invalid(self, form):
- # tl;dr -- this method is implemented to work around Django
- # ticket #25548, which is present in the Django 1.9 release
- # (but not in Django 1.8).
- #
- # The longer explanation is that in Django 1.9,
- # FormMixin.form_invalid() does not pass the form instance to
- # get_context_data(). This causes get_context_data() to
- # construct a new form instance with the same data in order to
- # put it into the template context, and then any access to
- # that form's ``errors`` or ``cleaned_data`` runs that form
- # instance's validation. The end result is that validation
- # gets run twice on an invalid form submission, which is
- # undesirable for performance reasons.
- #
- # Manually implementing this method, and passing the form
- # instance to get_context_data(), solves this issue (which was
- # fixed in Django 1.9.1 and will not be present in Django
- # 1.10).
- return self.render_to_response(self.get_context_data(form=form))
-
def get_form_kwargs(self):
# ContactForm instances require instantiation with an
# HttpRequest.
diff --git a/docs/conf.py b/docs/conf.py
index a171844..13ecbff 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -7,9 +7,9 @@ templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'django-contact-form'
-copyright = u'2007-2016, James Bennett'
-xversion = '1.3'
-release = '1.3'
+copyright = u'2007-2017, James Bennett'
+version = '1.4'
+release = '1.4.2'
exclude_trees = ['_build']
pygments_style = 'sphinx'
html_static_path = ['_static']
diff --git a/docs/faq.rst b/docs/faq.rst
index be9dbd2..4f9bbf6 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -11,13 +11,11 @@ you when installing, configuring or using django-contact-form.
What versions of Django and Python are supported?
-------------------------------------------------
-As of django-contact-form |version|, Django 1.8, 1.9, and 1.10 are
-supported, on Python 2.7, 3.3, 3.4 or 3.5. Although Django 1.8
-supported Python 3.2 at initial release, Python 3.2 is now at its
-end-of-life and django-contact-form no longer supports it.
-
-It is expected that django-contact-form |version| will also work
-without modification on Python 3.6 once it is released.
+As of django-contact-form |release|, Django 1.8, 1.10, and 1.11 are
+supported, on Python 2.7, 3.3, (Django 1.8 only), 3.4, 3.5, or 3.6
+(Django 1.11 only). Although Django 1.8 supported Python 3.2 at
+initial release, Python 3.2 is now at its end-of-life and
+django-contact-form no longer supports it.
What license is django-contact-form under?
@@ -41,27 +39,10 @@ reused, are essentially impossible to produce; variations in site
design, block structure, etc. cannot be reliably accounted for. As
such, django-contact-form provides bare-bones (i.e., containing no
HTML structure whatsoever) templates in its source distribution to
-enable running tests, and otherwise simply provides good documentation
+enable running tests, and otherwise just provides good documentation
of all required templates and the context made available to them.
-What happened to the spam-filtering form in previous versions?
---------------------------------------------------------------
-
-Older versions of django-contact-form shipped a subclass of
-:class:`~contact_form.forms.ContactForm` which used `the Akismet web
-service <http://akismet.com/>`_ to identify and reject spam
-submissions.
-
-Unfortunately, the Akismet Python library -- required in order to use
-such a class -- does not currently support all versions of Python on
-which django-contact-form is supported, meaning it cannot be
-included in django-contact-form by default. The author of
-django-contact-form is working on producing a version of the
-Akismet library compatible with Python 3, but it was not yet ready as
-of the release of django-contact-form |version|.
-
-
Why am I getting a bunch of ``BadHeaderError`` exceptions?
----------------------------------------------------------
@@ -77,8 +58,9 @@ allow spammers and other malicious users to manipulate email and
potentially cause automated systems to send mail to unintended
recipients), `Django's email-sending framework does not permit
newlines in message headers
-<https://docs.djangoproject.com/en/dev/topics/email/#preventing-header-injection>`_. ``BadHeaderError``
-is the exception Django raises when a newline is detected in a header.
+<https://docs.djangoproject.com/en/1.11/topics/email/#preventing-header-injection>`_.
+``BadHeaderError`` is the exception Django raises when a newline is
+detected in a header.
Note that this only applies to the headers of an email message; the
message body can (and usually does) contain newlines.
@@ -98,3 +80,34 @@ documentation for any changes made, and that following `PEP 8
requests without documentation won't be merged, and PEP 8 style
violations or test coverage below 100% are both configured to break
the build.
+
+
+I'm getting errors about "akismet" when trying to run tests?
+------------------------------------------------------------
+
+The full test suite of django-contact-form exercises all of its
+functionality, including the spam-filtering
+:class:`~contact_forms.forms.AkismetContactForm`. That class uses `the
+Wordpress Akismet spam-detection service <https://akismet.com/>`_ to
+perform spam filtering, and so requires the Python ``akismet`` module
+to communicate with the Akismet service, and some additional
+configuration (in the form of a valid Akismet API key and associated
+URL).
+
+By default, the tests for
+:class:`~contact_forms.forms.AkismetContactForm` will be skipped
+unless the required configuration (in the form of either a pair of
+Django settings, or a pair of environment variables) is
+detected. However, if you have supplied Akismet configuration but do
+*not* have the Python ``akismet`` module, you will see test errors
+from attempts to import ``akismet``. You can resolve this by running::
+
+ pip install akismet
+
+or (if you do not intend to use
+:class:`~contact_forms.forms.AkismetContactForm`) by no longer
+configuring the Django settings/environment variables used by Akismet.
+
+Additionally, if the :class:`~contact_forms.forms.AkismetContactForm`
+tests are skipped, the default code-coverage report will fail due to
+the relevant code not being exercised during the test run.
\ No newline at end of file
diff --git a/docs/forms.rst b/docs/forms.rst
index 9d34e93..200c8a6 100644
--- a/docs/forms.rst
+++ b/docs/forms.rst
@@ -1,17 +1,26 @@
.. _forms:
.. module:: contact_form.forms
+Contact form classes
+====================
+
+There are two contact-form classes included in django-contact-form;
+one provides all the infrastructure for a contact form, and will
+usually be the base class for subclasses which want to extend or
+modify functionality. The other is a subclass which adds spam
+filtering to the contact form.
+
The ContactForm class
-=====================
+---------------------
.. class:: ContactForm
The base contact form class from which all contact form classes
should inherit.
- If you don't need any customization, you can simply use this form
- to provide basic contact functionality; it will collect name,
+ If you don't need any customization, you can use this form to
+ provide basic contact-form functionality; it will collect name,
email address and message.
The :class:`~contact_form.views.ContactFormView` included in this
@@ -19,7 +28,7 @@ The ContactForm class
types of subclasses as well (see below for a discussion of the
important points), so in many cases it will be all that you
need. If you'd like to use this form or a subclass of it from one
- of your own views, just do the following:
+ of your own views, here's how:
1. When you instantiate the form, pass the current ``HttpRequest``
object as the keyword argument ``request``; this is used
@@ -42,7 +51,10 @@ The ContactForm class
order to make it easier to subclass and add functionality.
The following attributes play a role in determining behavior, and
- any of them can be implemented as an attribute or as a method:
+ any of them can be implemented as an attribute or as a method (for
+ example, if you wish to have ``from_email`` be dynamic, you can
+ implement a method named ``from_email()`` instead of setting the
+ attribute ``from_email``):
.. attribute:: from_email
@@ -66,8 +78,8 @@ The ContactForm class
The name of the template to use when rendering the body of the
message. By default, this is ``contact_form/contact_form.txt``.
- And two methods are involved in actually producing the contents of
- the message to send:
+ And two methods are involved in producing the contents of the
+ message to send:
.. method:: message()
@@ -126,12 +138,75 @@ The ContactForm class
.. method:: save
- If the form has data and is valid, will actually send the
- email, by calling :meth:`get_message_dict` and passing the
- result to Django's ``send_mail`` function.
+ If the form has data and is valid, will send the email, by
+ calling :meth:`get_message_dict` and passing the result to
+ Django's ``send_mail`` function.
Note that subclasses which override ``__init__`` or :meth:`save`
need to accept ``*args`` and ``**kwargs``, and pass them via
``super``, in order to preserve behavior (each of those methods
accepts at least one additional argument, and this application
- expects and requires them to do so).
\ No newline at end of file
+ expects and requires them to do so).
+
+
+The Akismet (spam-filtering) contact form class
+-----------------------------------------------
+
+.. class:: AkismetContactForm
+
+ A subclass of :class:`ContactForm` which adds spam filtering, via
+ `the Wordpress Akismet spam-detection service
+ <https://akismet.com/>`_.
+
+ Use of this class requires you to provide configuration for the
+ Akismet web service; you'll need to obtain an Akismet API key, and
+ you'll need to associate it with the site you'll use the contact
+ form on. You can do this at <https://akismet.com/>. Once you have,
+ you can configure in either of two ways:
+
+ 1. Put your Akismet API key in the Django setting
+ ``AKISMET_API_KEY``, and the URL it's associated with in the
+ setting ``AKISMET_BLOG_URL``, or
+
+ 2. Put your Akismet API key in the environment variable
+ ``PYTHON_AKISMET_API_KEY``, and the URL it's associated with in
+ the environment variable ``PYTHON_AKISMET_BLOG_URL``.
+
+ You will also need `the Python Akismet module
+ <http://akismet.readthedocs.io/>`_ to communicate with the Akismet
+ web service. You can install it by running ``pip install akismet``,
+ or django-contact-form can install it automatically for you if you
+ run ``pip install django-contact-form[akismet]``.
+
+ Once you have an Akismet API key and URL configured, and the
+ ``akismet`` module installed, you can drop in
+ ``AkismetContactForm`` anywhere you would have used
+ :class:`ContactForm`. For example, you could define a view
+ (subclassing :class:`~contact_form.views.ContactFormView`) like so,
+ and then point a URL at it:
+
+ .. code-block:: python
+
+ from contact_form.forms import AkismetContactForm
+ from contact_form.views import ContactFormView
+
+ class AkismetContactFormView(ContactFormView):
+ form_class = AkismetContactForm
+
+ Or directly specify the form in your URLconf:
+
+ .. code-block:: python
+
+ from django.conf.urls import url
+
+ from contact_form.forms import AkismetContactForm
+ from contact_form.views import ContactFormView
+
+ urlpatterns = [
+ # other URL patterns...
+ url(r'^contact-form/$',
+ ContactForm.as_view(
+ form_class=AkismetContactForm
+ ),
+ name='contact_form'),
+ ]
diff --git a/docs/index.rst b/docs/index.rst
index d98c031..e20b528 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,9 +1,8 @@
-django-contact-form |version|
+django-contact-form |release|
=============================
-``django-contact-form`` provides simple, customizable contact-form
-functionality for `Django <https://www.djangoproject.com/>`_-powered
-Web sites.
+django-contact-form provides customizable contact-form functionality
+for `Django <https://www.djangoproject.com/>`_-powered Web sites.
Basic functionality (collecting a name, email address and message) can
be achieved out of the box by setting up a few templates and adding
@@ -14,8 +13,8 @@ one line to your site's root URLconf:
url(r'^contact/', include('contact_form.urls')),
For notes on getting started quickly, and on how to customize
-``django-contact-form``'s behavior, read through the full
-documentation below.
+django-contact-form's behavior, read through the full documentation
+below.
Contents:
diff --git a/docs/install.rst b/docs/install.rst
index 57120fb..033fd02 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -10,17 +10,15 @@ information on obtaining and installing Django, consult the `Django
download page <https://www.djangoproject.com/download/>`_, which
offers convenient packaged downloads and installation instructions.
-The |version| release of django-contact-form supports Django 1.8,
-1.9, and 1.10, on the following Python versions:
+The |release| release of django-contact-form supports Django 1.8,
+1.10, and 1.11 on the following Python versions (matching the versions
+supported by Django itself):
-* Django 1.8 suports Python 2.7, 3.3, 3.4 and 3.5.
+* Django 1.8 supports Python 2.7, 3.3, 3.4, and 3.5.
-* Django 1.9 supports Python 2.7, 3.4 and 3.5.
+* Django 1.10 supports Python 2.7, 3.4, and 3.5.
-* Django 1.10 supports Python 2.7, 3.4 and 3.5.
-
-It is expected that django-contact-form |version| will work
-without modification on Python 3.6 once it is released.
+* Django 1.11 supports Python 2.7, 3.4, 3.5, and 3.6
.. important:: **Python 3.2**
@@ -40,49 +38,41 @@ the standard Python package-installation tool. If you don't have
Python 2.7.9 or later (for Python 2) or Python 3.4 or later (for
Python 3), ``pip`` came bundled with your installation of Python.
-Once you have ``pip``, simply type::
+Once you have ``pip``, type::
pip install django-contact-form
+If you plan to use the included spam-filtering contact form class,
+:class:`~contact_form.forms.AkismetContactForm`, you will also need
+the Python ``akismet`` module. You can manually install it via ``pip
+install akismet``, or tell ``django-contact-form`` to install it for
+you, by running::
-Manual installation
--------------------
-
-It's also possible to install django-contact-form manually. To do
-so, obtain the latest packaged version from `the listing on the Python
-Package Index
-<https://pypi.python.org/pypi/django-contact-form/>`_. Unpack the
-``.tar.gz`` file, and run::
-
- python setup.py install
-
-Once you've installed django-contact-form, you can verify
-successful installation by opening a Python interpreter and typing
-``import contact_form``.
-
-If the installation was successful, you'll simply get a fresh Python
-prompt. If you instead see an ``ImportError``, check the configuration
-of your install tools and your Python import path to ensure
-django-contact-form installed into a location Python can import
-from.
+ pip install django-contact-form[akismet]
Installing from a source checkout
---------------------------------
+If you want to work on django-contact-form, you can obtain a source
+checkout.
+
The development repository for django-contact-form is at
-<https://github.com/ubernostrum/django-contact-form>. Presuming you
-have `git <http://git-scm.com/>`_ installed, you can obtain a copy of
-the repository by typing::
+<https://github.com/ubernostrum/django-contact-form>. If you have `git
+<http://git-scm.com/>`_ installed, you can obtain a copy of the
+repository by typing::
... 283 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-contact-form.git
More information about the Python-modules-commits
mailing list