Ticket #8630: 8630_r9084_with_tests.diff
File 8630_r9084_with_tests.diff, 15.8 KB (added by , 16 years ago) |
---|
-
django/contrib/comments/admin.py
2 2 from django.conf import settings 3 3 from django.contrib.comments.models import Comment 4 4 from django.utils.translation import ugettext_lazy as _ 5 from django.contrib.comments import get_model 5 6 6 7 class CommentsAdmin(admin.ModelAdmin): 7 8 fieldsets = ( … … 22 23 ordering = ('-submit_date',) 23 24 search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address') 24 25 25 admin.site.register(Comment, CommentsAdmin) 26 if get_model() is Comment: 27 admin.site.register(Comment, CommentsAdmin) -
django/contrib/comments/__init__.py
2 2 from django.core import urlresolvers 3 3 from django.core.exceptions import ImproperlyConfigured 4 4 5 # Attributes required in the top-level app for COMMENTS_APP6 REQUIRED_COMMENTS_APP_ATTRIBUTES = ["get_model", "get_form", "get_form_target"]7 8 5 def get_comment_app(): 9 6 """ 10 7 Get the comment app (i.e. "django.contrib.comments") as defined in the settings … … 22 19 raise ImproperlyConfigured("The COMMENTS_APP setting refers to "\ 23 20 "a non-existing package.") 24 21 25 # Make sure some specific attributes exist inside that package.26 for attribute in REQUIRED_COMMENTS_APP_ATTRIBUTES:27 if not hasattr(package, attribute):28 raise ImproperlyConfigured("The COMMENTS_APP package %r does not "\29 "define the (required) %r function" % \30 (package, attribute))31 32 22 return package 33 23 34 24 def get_comment_app_name(): … … 39 29 return getattr(settings, 'COMMENTS_APP', 'django.contrib.comments') 40 30 41 31 def get_model(): 32 if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_model"): 33 return get_comment_app().get_model() 42 34 from django.contrib.comments.models import Comment 43 35 return Comment 44 36 45 37 def get_form(): 38 if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_form"): 39 return get_comment_app().get_form() 46 40 from django.contrib.comments.forms import CommentForm 47 41 return CommentForm 48 42 49 43 def get_form_target(): 44 if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_form_target"): 45 return get_comment_app().get_form_target() 50 46 return urlresolvers.reverse("django.contrib.comments.views.comments.post_comment") 51 47 52 48 def get_flag_url(comment): … … 55 51 """ 56 52 if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_flag_url"): 57 53 return get_comment_app().get_flag_url(comment) 58 else: 59 return urlresolvers.reverse("django.contrib.comments.views.moderation.flag", args=(comment.id,)) 54 return urlresolvers.reverse("django.contrib.comments.views.moderation.flag", args=(comment.id,)) 60 55 61 56 def get_delete_url(comment): 62 57 """ 63 58 Get the URL for the "delete this comment" view. 64 59 """ 65 60 if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_delete_url"): 66 return get_comment_app().get_flag_url(get_delete_url) 67 else: 68 return urlresolvers.reverse("django.contrib.comments.views.moderation.delete", args=(comment.id,)) 61 return get_comment_app().get_delete_url(comment) 62 return urlresolvers.reverse("django.contrib.comments.views.moderation.delete", args=(comment.id,)) 69 63 70 64 def get_approve_url(comment): 71 65 """ … … 73 67 """ 74 68 if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_approve_url"): 75 69 return get_comment_app().get_approve_url(comment) 76 else: 77 return urlresolvers.reverse("django.contrib.comments.views.moderation.approve", args=(comment.id,)) 70 return urlresolvers.reverse("django.contrib.comments.views.moderation.approve", args=(comment.id,)) -
tests/regressiontests/comment_tests/custom_comments_app/__init__.py
1 def get_model(): 2 from regressiontests.comment_tests.custom_comments_app.models import CustomComment 3 return CustomComment 4 5 # No need for a real Form or real reverse()d URLs, we're just testing 6 # that these functions are called. Simplicity is good. 7 8 def get_form(): 9 return 'FakeCustomForm' 10 11 def get_form_target(): 12 return '/my/form/target/' 13 14 def get_flag_url(c): 15 return '/my/flag/url/' 16 17 def get_delete_url(c): 18 return '/my/delete/url/' 19 20 def get_approve_url(c): 21 return '/my/approve/url/' -
tests/regressiontests/comment_tests/custom_comments_app/models.py
1 from django.db import models 2 3 class CustomComment(models.Model): 4 pass -
tests/regressiontests/comment_tests/tests/app_api_tests.py
2 2 from django.contrib import comments 3 3 from django.contrib.comments.models import Comment 4 4 from django.contrib.comments.forms import CommentForm 5 from django.test import TestCase 5 6 from regressiontests.comment_tests.tests import CommentTestCase 7 from regressiontests.comment_tests import custom_comments_app 8 from regressiontests.comment_tests.custom_comments_app.models import CustomComment 6 9 7 10 class CommentAppAPITests(CommentTestCase): 8 11 """Tests for the "comment app" API""" … … 28 31 c = Comment(id=12345) 29 32 self.assertEqual(comments.get_approve_url(c), "/approve/12345/") 30 33 34 class CommentAppCustomizationTests(TestCase): 35 """Tests that the COMMENTS_APP can customize form, model, etc""" 36 37 def setUp(self): 38 self.old_comments_app = getattr(settings, 'COMMENTS_APP', None) 39 self.old_installed_apps = settings.INSTALLED_APPS 40 settings.COMMENTS_APP = 'regressiontests.comment_tests.custom_comments_app' 41 settings.INSTALLED_APPS = tuple(list(settings.INSTALLED_APPS) 42 + [settings.COMMENTS_APP]) 43 44 def tearDown(self): 45 settings.COMMENTS_APP = self.old_comments_app 46 if settings.COMMENTS_APP is None: 47 delattr(settings._target, 'COMMENTS_APP') 48 settings.INSTALLED_APPS = self.old_installed_apps 49 50 def testGetCommentApp(self): 51 self.assertEqual(comments.get_comment_app(), custom_comments_app) 52 53 def testGetModel(self): 54 self.assertEqual(comments.get_model(), CustomComment) 55 56 def testGetForm(self): 57 self.assertEqual(comments.get_form(), 'FakeCustomForm') 58 59 def testGetFormTarget(self): 60 self.assertEqual(comments.get_form_target(), "/my/form/target/") 61 62 def testGetFlagURL(self): 63 c = Comment(id=12345) 64 self.assertEqual(comments.get_flag_url(c), "/my/flag/url/") 65 66 def getGetDeleteURL(self): 67 c = Comment(id=12345) 68 self.assertEqual(comments.get_delete_url(c), "/my/delete/url/") 69 70 def getGetApproveURL(self): 71 c = Comment(id=12345) 72 self.assertEqual(comments.get_approve_url(c), "/my/approve/url/") 73 -
docs/ref/contrib/comments/index.txt
175 175 176 176 <form action="{% comment_form_target %}" method="POST"> 177 177 178 .. _notes-on-the-comment-form: 179 178 180 Notes on the comment form 179 181 ------------------------- 180 182 … … 212 214 settings 213 215 signals 214 216 upgrade 215 217 custom -
docs/ref/contrib/comments/custom.txt
1 .. _ref-contrib-comments-custom: 2 3 ================================== 4 Customizing the comments framework 5 ================================== 6 7 Via the :setting:`COMMENTS_APP` setting, the comments framework allows 8 you to replace the built-in comment model and comment form with your 9 own classes. 10 11 The COMMENTS_APP 12 ================ 13 14 A custom :setting:`COMMENTS_APP` should define one or more of the 15 following module-level functions in it's ``__init__.py`` file. None of 16 these functions are required, so you can define any combination of 17 them (all others will use the defaults from 18 ``django.contrib.comments``): 19 20 .. function:: get_model() 21 22 Return the :class:`~django.db.models.Model` class to use for 23 comments. This model should inherit from 24 :class:`django.contrib.comments.models.BaseCommentAbstractModel`, 25 which defines necessary core fields. 26 27 The default implementation returns 28 :class:`django.contrib.comments.models.Comment`. 29 30 .. function:: get_form() 31 32 Return the :class:`~django.forms.Form` class you want to use for 33 creating, validating, and saving your comment model. Your custom 34 comment form should accept an additional first argument, 35 ``target_object``, which is the object the comment will be 36 attached to. 37 38 The default implementation returns 39 :class:`django.contrib.comments.forms.CommentForm`. 40 41 .. note:: 42 43 The default comment form also includes a number of unobtrusive 44 spam-prevention features (see 45 :ref:`notes-on-the-comment-form`). If replacing it with your 46 own form, you may want to look at the source code for the 47 built-in form and consider incorporating similar features. 48 49 .. function:: get_form_target() 50 51 Return the URL for POSTing comments. This will be the ``action`` 52 attribute when rendering your comment form. 53 54 The default implementation returns a reverse-resolved URL pointing 55 to the :func:`post_comment` view. 56 57 .. note:: 58 59 If you provide a custom comment model and/or form, but you 60 want to use the default :func:`post_comment` view, you will 61 need to be aware that it requires the model and form to have 62 certain additional attributes and methods: see the 63 :func:`post_comment` view documentation for details. 64 65 .. function:: get_flag_url() 66 67 Return the URL for the "flag this comment" view. 68 69 The default implementation returns a reverse-resolved URL pointing 70 to the :func:`django.contrib.comments.views.moderation.flag` view. 71 72 .. function:: get_delete_url() 73 74 Return the URL for the "delete this comment" view. 75 76 The default implementation returns a reverse-resolved URL pointing 77 to the :func:`django.contrib.comments.views.moderation.delete` view. 78 79 .. function:: get_approve_url() 80 81 Return the URL for the "approve this comment from moderation" view. 82 83 The default implementation returns a reverse-resolved URL pointing 84 to the :func:`django.contrib.comments.views.moderation.approve` view. 85 86 A sample custom comments app 87 ---------------------------- 88 89 A custom comments app might have an ``__init__.py`` like this:: 90 91 from django.core.urlresolvers import reverse 92 93 def get_model(): 94 from my_comments_app.models import MyComment 95 return MyComment 96 97 def get_form(): 98 from my_comments_app.forms import MyCommentForm 99 return MyCommentForm 100 101 def get_form_target(): 102 return reverse('my_comments_app.views.post_comment') 103 104 105 ``MyComment`` should inherit from :class:`BaseCommentAbstractModel`, 106 so in ``models.py``:: 107 108 from django.db import models 109 from django.contrib.comments.models import BaseCommentAbstractModel 110 111 class MyComment(BaseCommentAbstractModel): 112 ... fields and custom methods ... 113 114 And ``MyCommentForm`` should accept a target_object argument, 115 so in ``forms.py``:: 116 117 from django import forms 118 119 class MyCommentForm(forms.Form): 120 def __init__(self, target_object, data=None, initial=None): 121 ... 122 123 In order to enable this custom comments app, you would need to have 124 the following in your project's ``settings.py``:: 125 126 INSTALLED_APPS = ( 127 ... 128 'my_comments_app', 129 ... 130 ) 131 132 COMMENTS_APP = 'my_comments_app' 133 134 135 API reference 136 ================================== 137 138 BaseCommentAbstractModel 139 ------------------------ 140 141 .. class:: BaseCommentAbstractModel 142 143 :class:`BaseCommentAbstractModel` defines the following attributes 144 and methods, which your custom comment model will inherit: 145 146 .. attribute:: site 147 148 A foreign key to the 149 :class:`~django.contrib.sites.models.Site` model (see 150 :ref:`ref-contrib-sites`), defining which site this comment 151 appears on. 152 153 .. attribute:: content_type 154 155 A foreign key to the 156 :class:`~django.contrib.contenttypes.models.ContentType` 157 model. This is the content type of the target object the 158 comment is attached to. 159 160 .. attribute:: object_pk 161 162 The primary key of the target object. 163 164 .. attribute:: content_object 165 166 A 167 :class:`~django.contrib.contenttypes.generic.GenericForeignKey` 168 to the target object, using the :attr:`content_type` and 169 :attr:`object_pk` attributes. 170 171 .. method:: get_content_object_url() 172 173 Returns a URL that redirects to the URL of the comment's 174 target object. 175 176 post_comment view 177 ----------------- 178 179 .. function:: post_comment 180 181 The default :func:`post_comment` view requires the comment model 182 to have two additional fields: 183 184 .. attribute:: user 185 186 This should be a :class:`ForeignKey` to the :class:`User` 187 model. The view will store the user who posted the comment in 188 this field. 189 190 .. attribute:: ip_address 191 192 This should be an :class:`IPAddressField`. The 193 :func:`post_comment` view will store the remote IP address of 194 the comment poster in it. 195 196 The :func:`post_comment` view also requires the comment form to 197 have the following two methods: 198 199 .. method:: security_errors() 200 201 If this method returns a false value, the form will be 202 considered to have passed anti-spam screening. If it returns 203 any nonzero value, that value will be coerced to a string and 204 sent as part of an HTTP 400 (bad request) response. 205 206 .. method:: get_comment_object() 207 208 This method will only be called if the form has passed 209 validation and returned no :func:`security_errors()`. It 210 should return an (unsaved) comment object, which will be 211 annotated with the user and IP address and then saved. -
docs/ref/contrib/comments/settings.txt
29 29 COMMENTS_APP 30 30 ------------ 31 31 32 The app (i.e. entry in ``INSTALLED_APPS``) responsible for all "business logic." 33 You can change this to provide custom comment models and forms, though this is 34 currently undocumented. 32 An app which provides :ref:`customization of the comments framework 33 <ref-contrib-comments-custom>`. Use the same dotted-string notation 34 as in :setting:`INSTALLED_APPS`. Your custom :setting:`COMMENTS_APP` 35 must also be listed in :setting:`INSTALLED_APPS`.