Django自定义模板过滤器——如何提及用户和类别

Django Custom Template Filter - How To Mention Users And Categories

我正在制作一个社交网站,我希望能够使用 # 符号来提及用户或类别。 (示例:#User #Category #Other Category)。使用模板过滤器,我想首先找到字符串中的所有#,然后获取它后面的单词。然后我会检查类别的模型:

class Category(models.Model):
    name = models.CharField(max_length=250, unique=True)
    category_detail = models.TextField(max_length=1250, default='No information is known on this category.')

如果它与类别不匹配,我会检查# 后面的词是否是用户。如果它是一个类别,我想 link 使用 <a> 标签,如果它是用户,我也想 link 使用 <a> 标签。

urls:

urlpatterns = [
   path('u/@<str:username>', UserPostListView.as_view(), name='user-posts'),  # User Profile Link. Link This If After The # The Word Is A User
   path('c/<str:cats>/', views.category_view, name='category-detail'),  # View Category Link. Link This If After The # The Word Is A Category
]

用法: 回到这个例子,假设我有这样的评论:

这太棒了post! #some_user #类别#other_category

在我的模板中,我将其呈现为:

{{ comment.text|safe|filter_to_parse }}

我想要模板过滤器 filter_to_parse 到 return:

This is such a great post! <a href="/u/@some_user">#some_user</a> <a href="/c/category">#category</a> <a href="/c/other_category">#other_category</a> 

那么我如何在 Django 中制作模板过滤器来做到这一点?

我发现了一些可以帮助我做到这一点的东西:

但是这些 link 中的代码不起作用。谢谢。

编辑 2: 好的,感谢@flunder,我添加了 mark_safe 并尝试调试过滤器。这是我目前的工作代码:

from django import template
from django.template.defaultfilters import stringfilter
from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
register = template.Library()


@register.filter(name='mention', is_safe=True)
@stringfilter
def mention(value):
    my_list = value.split()
    for i in my_list:
        if i[0] == '@':
            try:
                stng = i[1:]
                user = User.objects.get(username=stng)
                if user:
                    profile_link = user.userprofile.get_absolute_url()
                    i = f"<a href='{profile_link}'>{i}</a>"
                    res = res + i + ' '
                    return mark_safe(res)
            except User.DoesNotExist:
                return value
        else:
            return value

这有效,并且在模板中它有一个 link 到用户配置文件,但缺少其余评论。示例:“@da 嗨!”变成了“@da”,没有嗨!这是因为它 returns res,它是标签和 space,所以我也需要帮助 return 阅读其余的评论。

此代码的另一个问题是,如果您提到多个用户,如@da @user2,那么只有第一个用户成为 url,其余的只是文本。

来自提到的评论:

 @register.filter(name='mention', is_safe=True)
  @stringfilter
  def mention(value):
     res = ""
     my_list = value.split()
     for i in my_list:
        if i[0] == '@':
            try:
              stng = i[1:]
              user = User.objects.get(username = stng)
              if user:
                 profile_link = user.userprofile.get_absolute_url()
                 i = f"<a href='{profile_link}'>{i}</a>"

            except User.DoesNotExist:
            print("Could not get the data")

      res = res + i + ' '
    
   return res

您是否加载了标签并使用了提及过滤器?

正确答案是:

from django.utils.safestring import mark_safe
@register.filter(name='mention', is_safe=True)
  @stringfilter
  def mention(value):
     res = ""
     my_list = value.split()
     for i in my_list:
        if i[0] == '@':
            try:
              stng = i[1:]
              user = User.objects.get(username = stng)
              if user:
                 profile_link = user.userprofile.get_absolute_url()
                 i = f"<a href='{profile_link}'>{i}</a>"

            except User.DoesNotExist:
            print("Could not get the data")

      res = res + i + ' '
    
   return mark_save(res)

mark_save:明确地将字符串标记为安全的(HTML)输出目的。返回的对象可以在任何适合字符串的地方使用。

这是完全未经测试的代码,但为了让您继续: def 提及(原件): listAts = 列表()

 for i in my_list:
    if i[0] == '@':
        try:
          stng = i[1:]
          user = User.objects.get(username = stng)
          if user:
             profile_link = user.userprofile.get_absolute_url()
             i = f"<a href='{profile_link}'>{i}</a>"

        except User.DoesNotExist:
        print("Could not get the data")

不是完整的功能代码,但可以帮助您入门:

 def mention(value):
     listOfAts = list()
     listOfSpaces = list()
     lastPosOfAt = 0
     while True:
       at = value.find("@", lastPosOfAt+1)
       if at != -1:
         lastPosOfAt = at
         listofAts.append(at)
       else:
         break

然后你有一个列表,其中包含 'value' 中“@”的所有位置。