基于当前用户的Django权限检查模板

Django permission check in template based on current user

给定一些模型:

class MailingList(models.Model):
    name = models.CharField(max_length=200, unique=True)

class Recipient(models.Model):
    email = models.CharField(max_length=200)
    mailingList = models.ForeignKey(MailingList)

class ListAdministrator(models.Model):
    username = models.CharField(max_length=200)
    mailingList = models.ForeignKey(MailingList)

我想以干净的 MVC 方式完成以下操作,而无需不必要的代码重复:当前登录的用户只能从 MailingList 中删除 Recipient,如果他被列为ListAdministrators 或者是超级用户。

权限检查应该在两个地方进行:

  1. 视图 deleteRecipient(request, recipient_id) 应在删除收件人之前检查所需的权限。这部分很简单,所有需要的信息都在 request.userRecipient.objects.get(pk=recipient_id).mailingListlistadministrator_set 中。

  2. 在列出所有邮件列表和所有相应收件人的模板中,只有那些允许当前用户删除的邮件列表应该在它们旁边显示 "Delete Recipient" link。这是我挣扎的地方:

    • 我不能给模型一个方法 userMayDelete(self, user) 并从传递当前用户的模板调用该方法,因为 Django 不允许从模板传递参数。
    • 我无法在没有用户参数的情况下为模型提供方法 currentUserMayDelete(self),因为模型无法隐式访问当前用户。
    • 向模板传递两组邮件列表,用户可以从中删除收件人的邮件列表和用户不能从中删除收件人的邮件列表似乎很麻烦。而且它不允许按字母顺序组合显示所有列表。

完成此任务的干净(最好是简单)方法是什么?

您可以在将 MailingList 实例传递给模板之前为它设置附加属性:

def show_lists(request):

    username = request.user.username
    lists_under_control = [la.mailingList for la in
                           ListAdministrator.objects.filter(username=username)]

    mailing_lists = list(MailingList.objects.all())
    for mailing_list in mailing_lists:
        mailing_list.user_may_delete = (mailing_list in lists_under_control)

    return render(request, 'app/mailing_lists.html',
                           {'mailing_lists': mailing_lists})

然后在模板中使用这个属性:

<ul>
{% for mailing_list in mailing_lists %}
    <li>{{ mailing_list }}
        <ul>
        {% for recipient in mailing_list.recipient_set.all %}
            <li>{{ recipient.email }}
                {% if mailing_list.user_may_delete %}
                    <a href="{% url 'delete_recipient' recipient.id %}">
                        Delete recipient
                    </a>
                {% endif %}
            </li>
        {% endfor %}
        </ul>
    </li>

{% endfor %}
</ul>

另一个(更糟糕的)选项是创建模板过滤器并像这样使用它:

{% if user|may_delete:mailing_list %}