Django - edytowalne komentarze

Domyślnie w django 1.0.2 w wbudowanych komentarzach nie ma możliwości edytowania komentarzy, brakowało mi tej funkcjonalności, więc postanowiłem rozszerzyć istniejące komentarze o edycje komentarzy. Zauważyłem także że nowe komentarze dodane w django 1.0 nie posiadają funkcji uzupełniania danych użytkownika(jeżeli jest zalogowany), podczas dodawania komentarza mimo iż model Comment jest powiązany teraz z modelem User z aplikacji auth. Rozszerzyłem więc komentarze o tą funkcjonalność. Po dyskusji na ten temat na kanale #django-pl został utworzony ticket na stronie projektu Django z tym związany.

Zależało mi aby używać tego samego mechanizmu zarówno do edycji komentarzy przez zalogowanych użytkowników jak i przy dodawaniu nowych komentarzy. Aby to zrealizować wystarczy stworzyć własnego tag'a który będzie używać klasy po dziedziczonej po klasie RenderCommentFormNode, tag'a ten będzie używany do generowania formularza dodawania lub edycji komentarza, zamiast domyślnego tag'a.

from django.contrib.comments.templatetags.comments import RenderCommentFormNode

class EditRenderCommentFormNode(RenderCommentFormNode):
    """Render the comment form directly"""

    def get_form(self, context):

        ctype, object_pk = self.get_target_ctype_pk(context)
        if object_pk:
            comment = context.get('comment', None)
            if comment:
                initial = dict(name=comment.user_name,
                        email=comment.user_email,
                        url=comment.user_url,
                        comment=comment.comment 
                        )
            else:
                request = context['request']
                initial = dict(name=request.user.get_full_name() or request.user.username,
                               email=request.user.email,
                               )

            return EditCommentForm(target_object=ctype.get_object_for_this_type(pk=object_pk), 
                                   initial=initial)
        else:
            return None

@register.tag
def render_edit_comment_form(parser, token):
    """
    Render the comment form (as returned by ``{% render_edit_comment_form %}``) through
    the ``comments/form.html`` template.

    Syntax::

        {% render_edit_comment_form for [object] %}
        {% render_edit_comment_form for [app].[model] [object_id] %}
    """
    return EditRenderCommentFormNode.handle_token(parser, token)

Teraz zamiast stosowania tag'a render_comment_form będziemy używać tag'a render_edit_comment_form

W po dziedziczonej klasie RenderCommentFormNode została nadpisana metoda get_form która teraz zwraca klasę formularza EditCommentForm. Dostaje on teraz obiekt komentarza (w przypadku edycji) oprócz standardowych parametrów przekazywanych do klasy CommentForm.

class EditCommentForm(CommentForm):
    def __init__(self, comment_instance=None, *args, **kwargs):
        self.comment_instance = comment_instance

        super(EditCommentForm, self).__init__(*args, **kwargs)

    def get_comment_object(self):
        if self.comment_instance:
            self.comment_instance.user_name = self.cleaned_data['name']
            self.comment_instance.user_email = self.cleaned_data['email']
            self.comment_instance.user_url = self.cleaned_data['url']
            self.comment_instance.comment = self.cleaned_data['comment']

            return self.comment_instance
        else:
            return super(EditCommentForm, self).get_comment_object()

Nowa klasa formularza edycji komentarzy posiada nadpisaną metodę get_comment_object która, w przypadku edycji zwraca istniejący zmieniony komentarz a w przypadku dodawania nowego komentarza zwraca nowy komentarz.

Zostało jeszcze teraz napisać widok który będzie odpowiadać za edycje komentarzy:

@login_required
def edit_comment(request, comment_id):
    comment = get_object_or_404(Comment, pk=comment_id)

    if comment.user != request.user:
        return HttpResponseForbidden('Access forbidden!!!')

    if request.POST:
        form = EditCommentForm(comment_instance=comment, target_object=comment.content_object, data=request.POST)
        if form.is_valid():
            edited_comment = form.get_comment_object()
            edited_comment.save()
            return HttpResponseRedirect(comment.get_absolute_url())

    return render_to_response('comments/edit_comment.html', 
                              {'comment':comment}, 
                              context_instance=RequestContext(request)
                              )

Widok ten należy jeszcze podpiąć w urls.py, np:

urlpatterns = patterns('', 
    (r'^your-comments/edit/(?P<comment_id>[0-9]+)/$', 'dreamblog.blog.views.edit_comment'),    
)

Na końcu musimy jeszcze stworzyć szablon: comments/form.html który będzie wykorzystywany do generowania formularza przez tag render_comment_form. Może np. wyglądać tak:

{% load i18n blog_tags %}
{% for field in form %}
{% if field.is_hidden %}
  {{ field }}
{% else %}
  <p
    {% if field.errors %} class="error"{% endif %}
    {% ifequal field.name "honeypot" %} style="display:none;"{% endifequal %}>
    {% if field.errors %}{{ field.errors }}{% endif %}
    {{ field.label_tag }}
    {{ field }} 
    {% if field.help_text %} 
        <span id="comment_help_text">{{ field.help_text }}</span>   
    {% endif %}
  </p>
{% endif %}
{% endfor %}
<div style="margin-left: 150px;">
<input type="submit" name="post" class="submit-post" value="{% trans "Post" %}" />
{% if not comment %}
<input type="submit" name="preview" class="submit-preview" value="{% trans "Preview" %}" />
{% endif %}
</div>

W django 1.1 wbudowane komentarze będą umożliwiały dużo łatwiejszy sposób na rozszerzanie ich - http://docs.djangoproject.com/en/dev/ref/contrib/comments/custom/

Jakieś pytania/sugestie ? komentarze ?


Comments turned off