Ticket #8630: 8630_new.diff

File 8630_new.diff, 17.3 KB (added by Thejaswi Puthraya, 11 years ago)

git-patch that has tests, docs and the patch. Slight change from Carljm's patch.

  • django/contrib/comments/__init__.py

    diff --git a/django/contrib/comments/__init__.py b/django/contrib/comments/__init__.py
    index 55b8bac..6a0287b 100644
    a b from django.conf import settings 
    22from django.core import urlresolvers
    33from django.core.exceptions import ImproperlyConfigured
    44
    5 # Attributes required in the top-level app for COMMENTS_APP
    6 REQUIRED_COMMENTS_APP_ATTRIBUTES = ["get_model", "get_form", "get_form_target"]
    7 
    85def get_comment_app():
    96    """
    107    Get the comment app (i.e. "django.contrib.comments") as defined in the settings
    def get_comment_app(): 
    2219        raise ImproperlyConfigured("The COMMENTS_APP setting refers to "\
    2320                                   "a non-existing package.")
    2421
    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 
    3222    return package
    3323
    3424def get_comment_app_name():
    def get_comment_app_name(): 
    3929    return getattr(settings, 'COMMENTS_APP', 'django.contrib.comments')
    4030
    4131def get_model():
     32    if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_model"):
     33        return get_comment_app().get_model()
    4234    from django.contrib.comments.models import Comment
    4335    return Comment
    4436
    4537def get_form():
     38    if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_form"):
     39        return get_comment_app().get_form()
    4640    from django.contrib.comments.forms import CommentForm
    4741    return CommentForm
    4842
    4943def 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()
    5046    return urlresolvers.reverse("django.contrib.comments.views.comments.post_comment")
    5147
    5248def get_flag_url(comment):
    def get_flag_url(comment): 
    5551    """
    5652    if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_flag_url"):
    5753        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,))
    6055
    6156def get_delete_url(comment):
    6257    """
    6358    Get the URL for the "delete this comment" view.
    6459    """
    6560    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(get_delete_url)
     62    return urlresolvers.reverse("django.contrib.comments.views.moderation.delete", args=(comment.id,))
    6963
    7064def get_approve_url(comment):
    7165    """
    def get_approve_url(comment): 
    7367    """
    7468    if get_comment_app_name() != __name__ and hasattr(get_comment_app(), "get_approve_url"):
    7569        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,))
  • django/contrib/comments/admin.py

    diff --git a/django/contrib/comments/admin.py b/django/contrib/comments/admin.py
    index 11271c9..e2dfb67 100644
    a b  
    11from django.contrib import admin
    22from django.contrib.comments.models import Comment
    33from django.utils.translation import ugettext_lazy as _
     4from django.contrib.comments import get_model
    45
    56class CommentsAdmin(admin.ModelAdmin):
    67    fieldsets = (
    class CommentsAdmin(admin.ModelAdmin): 
    2122    ordering = ('-submit_date',)
    2223    search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address')
    2324
    24 admin.site.register(Comment, CommentsAdmin)
     25if get_model() is Comment:
     26    admin.site.register(Comment, CommentsAdmin)
  • new file docs/ref/contrib/comments/custom.txt

    diff --git a/docs/ref/contrib/comments/custom.txt b/docs/ref/contrib/comments/custom.txt
    new file mode 100644
    index 0000000..7da3700
    - +  
     1.. _ref-contrib-comments-custom:
     2
     3==================================
     4Customizing the comments framework
     5==================================
     6
     7Via the :setting:`COMMENTS_APP` setting, the comments framework allows
     8you to replace the built-in comment model and comment form with your
     9own classes.
     10
     11The COMMENTS_APP
     12================
     13
     14A custom :setting:`COMMENTS_APP` should define one or more of the
     15following module-level functions in it's ``__init__.py`` file. None of
     16these functions are required, so you can define any combination of
     17them (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
     86A sample custom comments app
     87----------------------------
     88
     89A 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`,
     106so 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
     114And ``MyCommentForm`` should accept a target_object argument,
     115so 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
     123In order to enable this custom comments app, you would need to have
     124the 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
     135API reference
     136==================================
     137
     138BaseCommentAbstractModel
     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
     176post_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/index.txt

    diff --git a/docs/ref/contrib/comments/index.txt b/docs/ref/contrib/comments/index.txt
    index 5aeebe3..55140ce 100644
    a b Be sure to read the `notes on the comment form`_, below, for some special 
    164164considerations you'll need to make if you're using this aproach.
    165165
    166166.. templatetag:: comment_form_target
     167.. _notes-on-the-comment-form:
    167168
    168169Getting the comment form target
    169170~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    More information 
    212213   settings
    213214   signals
    214215   upgrade
    215 
     216   custom
  • docs/ref/contrib/comments/settings.txt

    diff --git a/docs/ref/contrib/comments/settings.txt b/docs/ref/contrib/comments/settings.txt
    index 2aa392c..ff94d2d 100644
    a b this will be rejected. Defaults to 3000. 
    2929COMMENTS_APP
    3030------------
    3131
    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.
     32An app which provides :ref:`customization of the comments framework
     33<ref-contrib-comments-custom>`.  Use the same dotted-string notation
     34as in :setting:`INSTALLED_APPS`.  Your custom :setting:`COMMENTS_APP`
     35must also be listed in :setting:`INSTALLED_APPS`.
  • new file tests/regressiontests/comment_tests/custom_comments/__init__.py

    diff --git a/tests/regressiontests/comment_tests/custom_comments/__init__.py b/tests/regressiontests/comment_tests/custom_comments/__init__.py
    new file mode 100644
    index 0000000..d644b53
    - +  
     1from django.core import urlresolvers
     2
     3def get_model():
     4    from regressiontests.comment_tests.custom_comments.models import CustomComment
     5    return CustomComment
     6
     7def get_form():
     8    from regressiontests.comment_tests.custom_comments.forms import CustomCommentForm
     9    return CustomCommentForm
     10
     11def get_form_target():
     12    return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.submit_comment")
     13
     14def get_flag_url(c):
     15    return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.flag_comment", args=(c.id,))
     16
     17def get_delete_url(c):
     18    return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.delete_comment", args=(c.id,))
     19
     20def get_approve_url(c):
     21    return urlresolvers.reverse("regressiontests.comment_tests.custom_comments.views.approve_comment", args=(c.id,))
  • new file tests/regressiontests/comment_tests/custom_comments/forms.py

    diff --git a/tests/regressiontests/comment_tests/custom_comments/forms.py b/tests/regressiontests/comment_tests/custom_comments/forms.py
    new file mode 100644
    index 0000000..b788cdc
    - +  
     1from django import forms
     2
     3class CustomCommentForm(forms.Form):
     4    pass
  • new file tests/regressiontests/comment_tests/custom_comments/models.py

    diff --git a/tests/regressiontests/comment_tests/custom_comments/models.py b/tests/regressiontests/comment_tests/custom_comments/models.py
    new file mode 100644
    index 0000000..592ad79
    - +  
     1from django.db import models
     2
     3class CustomComment(models.Model):
     4    pass
  • new file tests/regressiontests/comment_tests/custom_comments/views.py

    diff --git a/tests/regressiontests/comment_tests/custom_comments/views.py b/tests/regressiontests/comment_tests/custom_comments/views.py
    new file mode 100644
    index 0000000..04a1d23
    - +  
     1def submit_comment(request):
     2    return None
     3
     4def flag_comment(request, comment_id):
     5    return None
     6
     7def delete_comment(request, comment_id):
     8    return None
     9
     10def approve_comment(request, comment_id):
     11    return None
  • tests/regressiontests/comment_tests/tests/app_api_tests.py

    diff --git a/tests/regressiontests/comment_tests/tests/app_api_tests.py b/tests/regressiontests/comment_tests/tests/app_api_tests.py
    index d4a4488..19b9f1a 100644
    a b class CommentAppAPITests(CommentTestCase): 
    2828        c = Comment(id=12345)
    2929        self.assertEqual(comments.get_approve_url(c), "/approve/12345/")
    3030
     31
     32class 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/")
  • new file tests/regressiontests/comment_tests/urls.py

    diff --git a/tests/regressiontests/comment_tests/urls.py b/tests/regressiontests/comment_tests/urls.py
    new file mode 100644
    index 0000000..b72f677
    - +  
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = 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
Back to Top