diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py
index c263ea7d10c..bc4d9324647 100644
--- a/django/contrib/comments/models.py
+++ b/django/contrib/comments/models.py
@@ -60,7 +60,7 @@ class Comment(BaseCommentAbstractModel):
# Metadata about the comment
submit_date = models.DateTimeField(_('date/time submitted'), default=None)
- ip_address = models.IPAddressField(_('IP address'), blank=True, null=True)
+ ip_address = models.GenericIPAddressField(_('IP address'), unpack_ipv4=True, blank=True, null=True)
is_public = models.BooleanField(_('is public'), default=True,
help_text=_('Uncheck this box to make the comment effectively ' \
'disappear from the site.'))
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index ce1e6439461..497a0349db4 100644
--- a/docs/releases/1.6.txt
+++ b/docs/releases/1.6.txt
@@ -149,6 +149,30 @@ Backwards incompatible changes in 1.6
{{ title }}{# Translators: Extracted and associated with 'Welcome' below #}
{% trans "Welcome" %}
+* The :doc:`comments ` app now uses a ``GenericIPAddressField``
+ for storing commenters' IP addresses, to support comments submitted from IPv6 addresses.
+ Until now, it stored them in an ``IPAddressField``, which is only meant to support IPv4.
+ When saving a comment made from an IPv6 address, the address would be silently truncated
+ on MySQL databases, and raise an exception on Oracle.
+ You will need to change the column type in your database to benefit from this change.
+
+ For MySQL, execute this query on your project's database:
+
+ .. code-block:: sql
+
+ ALTER TABLE django_comments MODIFY ip_address VARCHAR(39);
+
+ For Oracle, execute this query:
+
+ .. code-block:: sql
+
+ ALTER TABLE DJANGO_COMMENTS MODIFY (ip_address VARCHAR2(39));
+
+ If you do not apply this change, the behaviour is unchanged: on MySQL, IPv6 addresses
+ are silently truncated; on Oracle, an exception is generated. No database
+ change is needed for SQLite or PostgreSQL databases.
+
+
.. warning::
In addition to the changes outlined in this section, be sure to review the
diff --git a/tests/regressiontests/comment_tests/tests/comment_view_tests.py b/tests/regressiontests/comment_tests/tests/comment_view_tests.py
index 5c1954026d0..0d994d3af8b 100644
--- a/tests/regressiontests/comment_tests/tests/comment_view_tests.py
+++ b/tests/regressiontests/comment_tests/tests/comment_view_tests.py
@@ -101,13 +101,43 @@ class CommentViewTests(CommentTestCase):
settings.DEBUG = olddebug
def testCreateValidComment(self):
+ address = "1.2.3.4"
a = Article.objects.get(pk=1)
data = self.getValidData(a)
- self.response = self.client.post("/post/", data, REMOTE_ADDR="1.2.3.4")
+ self.response = self.client.post("/post/", data, REMOTE_ADDR=address)
self.assertEqual(self.response.status_code, 302)
self.assertEqual(Comment.objects.count(), 1)
c = Comment.objects.all()[0]
- self.assertEqual(c.ip_address, "1.2.3.4")
+ self.assertEqual(c.ip_address, address)
+ self.assertEqual(c.comment, "This is my comment")
+
+ def testCreateValidCommentIPv6(self):
+ """
+ Test creating a valid comment with a long IPv6 address.
+ Note that this test should fail when Comment.ip_address is an IPAddress instead of a GenericIPAddress,
+ but does not do so on SQLite or PostgreSQL, because they use the TEXT and INET types, which already
+ allow storing an IPv6 address internally.
+ """
+ address = "2a02::223:6cff:fe8a:2e8a"
+ a = Article.objects.get(pk=1)
+ data = self.getValidData(a)
+ self.response = self.client.post("/post/", data, REMOTE_ADDR=address)
+ self.assertEqual(self.response.status_code, 302)
+ self.assertEqual(Comment.objects.count(), 1)
+ c = Comment.objects.all()[0]
+ self.assertEqual(c.ip_address, address)
+ self.assertEqual(c.comment, "This is my comment")
+
+ def testCreateValidCommentIPv6Unpack(self):
+ address = "::ffff:18.52.18.52"
+ a = Article.objects.get(pk=1)
+ data = self.getValidData(a)
+ self.response = self.client.post("/post/", data, REMOTE_ADDR=address)
+ self.assertEqual(self.response.status_code, 302)
+ self.assertEqual(Comment.objects.count(), 1)
+ c = Comment.objects.all()[0]
+ # We trim the '::ffff:' bit off because it is an IPv4 addr
+ self.assertEqual(c.ip_address, address[7:])
self.assertEqual(c.comment, "This is my comment")
def testPostAsAuthenticatedUser(self):