Search code examples
djangoformstemplatestagscsrf

Template Tag not working with csrf_token


I am attempting to create a custom template tag to display a comment. When I try submitting a reply through the form attached to each comment, I get an error:

Forbidden (403) CSRF verification failed. Request aborted. Reason given for failure: CSRF token missing or incorrect.

The csrf token works on the form with the download and follow button, but not on the form generated from the template tag. Any help would be appreciated.

DocumentView

def DocumentView(request, doc_id):
    context = {}
    user = None
    if request.user.is_authenticated():
        email = request.user.email
        id = request.user.id
        user = get_user_information(email=email)
    else:
        user = None

    context['user'] = user

    if request.method == 'POST':
        user_id = request.POST.get('submit', '')


    doc = Doc.objects.get(id=doc_id)
    doc_version = DocVersion.objects.filter(doc__id=doc_id).latest('id')
    doc_title = doc.response_title
    allow_comments = doc.allow_comments
    comments = {}
    if user['user_type'] == 'DA':
        for comment in Comment.objects.filter(doc_id=doc_id):
            comments[comment.id] = {'content': comment.content, 'reply_to': comment.reply_to, 'user_id': comment.user.id}
    else:
        for comment in Comment.objects.filter(doc_id=doc_id, is_flagged=False):
            comments[comment.id] = {'content': comment.content, 'reply_to': comment.reply_to, 'user_id': comment.user.id}
    context['comments'] = comments
    context['allow_comments'] = allow_comments
    context['doc_title'] = doc_title
    print context
    return render(request, 'doc/document.html', context)

comment_tags.py

from django.template import Library, Node, Context, Variable
from django.template.loader import get_template
from django import template

register = Library()

@register.tag()
def comments(parser, token):
    user_id, profile_pic, reply_id, comment_id, content, info = None, None, None, None, "", ""

    for index, x in enumerate(token.split_contents()):
        if x == 'user_id':
            user_id = token.split_contents()[index + 1]
        if x == 'profile_pic':
            profile_pic = token.split_contents()[index + 1]
        if x == 'content':
            content = token.split_contents()[index + 1]
        if x == 'info':
            info = token.split_contents()[index + 1]
        if x == 'reply_id':
            reply_id = token.split_contents()[index + 1]
        if x == 'comment_id':
            comment_id = token.split_contents()[index + 1]

    return CommentNode(user_id, profile_pic, comment_id, content, info, reply_id)




class CommentNode(template.Node):
    def __init__(self, user_id, profile_pic, comment_id, content, info, reply_id):
        self.user_id = template.Variable(user_id)
        self.profile_pic = template.Variable(profile_pic)
        self.content = template.Variable(content)
        self.info = template.Variable(info)
        self.comment_id = template.Variable(comment_id)

        if reply_id != None:
            self.reply_id = template.Variable(reply_id)
        else:
            self.reply_id = reply_id

    def render(self, context):
        t = get_template("srd/comment.html")

        if self.reply_id == None:
            return t.render({
                    'user_id': self.user_id.resolve(context),
                    'profile_pic': self.profile_pic.resolve(context),
                    'comment_id': self.comment_id.resolve(context),
                    'content': self.content.resolve(context),
                    'info': self.info.resolve(context),
                    'reply_id': self.reply_id
                }
            )
        else:
            return t.render({
                'user_id': self.user_id.resolve(context),
                'profile_pic': self.profile_pic.resolve(context),
                'comment_id': self.comment_id.resolve(context),
                'content': self.content.resolve(context),
                'info': self.info.resolve(context),
                'reply_id': self.reply_id.resolve(context)
            })

document.html

{% load static %}
{% load comment_tags %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ doc_title }}</title>
    <link rel="stylesheet" type="text/css" href="{% static "stylesheets/doc.css" %}">
    <script src="{% static "javascript/jquery-3.2.1.min.js" %}" type="text/javascript"></script>
    <script src="{% static "javascript/doc.js" %}" type="text/javascript"></script>

</head>
<body>
    <div class="main_div">
        <div class="content_div">
            <div class="info_div">
                <h2>[{{ generic_name }}]</h2>
                <label>{{ doc_title }} <a href="edit">[Edit]</a></label>

            </div>
            <div class="doc_div">
                <div class="doc_preview">
                </div>
                <div class="doc_preview_info">
                    <form method="post">
                        {% csrf_token %}
                        <input type="hidden" value="{{ user.id }}" />
                        <input type="submit" value="Donwload PDF" /><br/>
                        <input type="submit" value="Follow" />
                    </form>

                </div>
            </div>
            {% if allow_comments %}
            <div class="comment_div" id="comment_div">
                {% for commentid, comment in comments.items %}
                    {% if comment.reply_to %}
                        {% comments comment_id commentid user_id comment.user_id reply_id comment.reply_to.id profile_pic "hi" content comment.content info "XYZ" %}
                    {% else %}
                        {% comments comment_id commentid user_id comment.user_id profile_pic "hi" content comment.content info "XYZ" %}
                    {% endif %}
                {% endfor %}
            </div>
            {% endif %}
        </div>
        <div class="ad_div">
            <div class="ad"></div>
            <div class="ad"></div>
            <div class="ad"></div>
            <div class="ad"></div>
        </div>
    </div>
</body>
</html>

comment.html

<div class="comment">
    <div class="profile_pic">
    {{ user_id }}
    </div>
    <div class="comment_content">
        <div class="top_bar">
            <div class="comment_info">
                {{ info }}
            </div>
            <div class="flag">
                <a href="#">Flag</a>
            </div>
        </div>
        <div class="comment_text">
            {{ content }}
        </div>
        <div class="reply_bar">
            <a id="reply_link_{{ comment_id }}" class="reply_link" href="#">Reply</a>
        </div>
    </div>
</div>
<div class="reply" id="reply_{{ comment_id }}">
    <form method="post">
        {% csrf_token %}
        <input type="hidden" value="{{ reply_id }}"/>
        <input name="reply_text" type="text" />
        <input type="submit" value="Post" />
    </form>
</div>

Solution

  • Since you are rendering the template manually in your tag, you need to use a RequestContext so that it runs the context processors - including the one that adds the CSRF token.

    return t.render(RequestContext(context['request'], {
                         ...
                    }))
    

    Note, the whole thing could be greatly simplified by using an inclusion tag.