diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py index c263ea7d10..bc4d932464 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 c2a3d56c53..c0eabde570 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -152,6 +152,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 5c1954026d..0d994d3af8 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):