Ticket #8630: 8630_r9781.diff
File 8630_r9781.diff, 17.2 KB (added by , 16 years ago) |
---|
-
django/contrib/comments/admin.py
1 1 from django.contrib import admin 2 2 from django.contrib.comments.models import Comment 3 3 from django.utils.translation import ugettext_lazy as _ 4 from django.contrib.comments import get_model 4 5 5 6 class CommentsAdmin(admin.ModelAdmin): 6 7 fieldsets = ( … … 21 22 ordering = ('-submit_date',) 22 23 search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address') 23 24 24 admin.site.register(Comment, CommentsAdmin) 25 if get_model() is Comment: 26 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/views.py
1 def submit_comment(request): 2 return None 3 4 def flag_comment(request, comment_id): 5 return None 6 7 def delete_comment(request, comment_id): 8 return None 9 10 def approve_comment(request, comment_id): 11 return None -
tests/regressiontests/comment_tests/custom_comments/__init__.py
1 from django.core import urlresolvers 2 3 def get_model(): 4 from regressiontests.comment_tests.custom_comments.models import CustomComment 5 return CustomComment 6 7 def get_form(): 8 from regressiontests.comment_tests.custom_comments.forms import CustomCommentForm 9 return CustomCommentForm 10 11 def get_form_target(): 12 return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.submit_comment") 13 14 def get_flag_url(c): 15 return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.flag_comment", args=(c.id,)) 16 17 def get_delete_url(c): 18 return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.delete_comment", args=(c.id,)) 19 20 def get_approve_url(c): 21 return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.approve_comment", args=(c.id,)) -
tests/regressiontests/comment_tests/custom_comments/models.py
1 from django.db import models 2 3 class CustomComment(models.Model): 4 pass -
tests/regressiontests/comment_tests/custom_comments/forms.py
1 from django import forms 2 3 class CustomCommentForm(forms.Form): 4 pass -
tests/regressiontests/comment_tests/tests/app_api_tests.py
28 28 c = Comment(id=12345) 29 29 self.assertEqual(comments.get_approve_url(c), "/approve/12345/") 30 30 31 32 class CustomCommentTest(CommentTestCase): 33 urls = 'regressiontests.comment_tests.urls' 34 35 def setUp(self): 36 self.old_comments_app = getattr(settings, 'COMMENTS_APP', None) 37 settings.COMMENTS_APP = 'regressiontests.comment_tests.custom_comments' 38 settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + [settings.COMMENTS_APP,] 39 40 def tearDown(self): 41 del settings.INSTALLED_APPS[-1] 42 settings.COMMENTS_APP = self.old_comments_app 43 if settings.COMMENTS_APP is None: 44 delattr(settings._target, 'COMMENTS_APP') 45 46 def testGetCommentApp(self): 47 from regressiontests.comment_tests import custom_comments 48 self.assertEqual(comments.get_comment_app(), custom_comments) 49 50 def testGetModel(self): 51 from regressiontests.comment_tests.custom_comments.models import CustomComment 52 self.assertEqual(comments.get_model(), CustomComment) 53 54 def testGetForm(self): 55 from regressiontests.comment_tests.custom_comments.forms import CustomCommentForm 56 self.assertEqual(comments.get_form(), CustomCommentForm) 57 58 def testGetFormTarget(self): 59 self.assertEqual(comments.get_form_target(), "/post/") 60 61 def testGetFlagURL(self): 62 c = Comment(id=12345) 63 self.assertEqual(comments.get_flag_url(c), "/flag/12345/") 64 65 def getGetDeleteURL(self): 66 c = Comment(id=12345) 67 self.assertEqual(comments.get_delete_url(c), "/delete/12345/") 68 69 def getGetApproveURL(self): 70 c = Comment(id=12345) 71 self.assertEqual(comments.get_approve_url(c), "/approve/12345/") -
tests/regressiontests/comment_tests/urls.py
1 from django.conf.urls.defaults import * 2 3 urlpatterns = patterns('regressiontests.comment_tests.custom_comments.views', 4 url(r'^post/$', 'submit_comment'), 5 url(r'^flag/(\d+)/$', 'flag_comment'), 6 url(r'^delete/(\d+)/$', 'delete_comment'), 7 url(r'^approve/(\d+)/$', 'approve_comment'), 8 ) 9 -
docs/ref/contrib/comments/index.txt
164 164 considerations you'll need to make if you're using this aproach. 165 165 166 166 .. templatetag:: comment_form_target 167 .. _notes-on-the-comment-form: 167 168 168 169 Getting the comment form target 169 170 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ … … 212 213 settings 213 214 signals 214 215 upgrade 215 216 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 ``my_comments_app/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 ``my_comments_app/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`.