如何在 django QuerySet 对象中拆分字符串

Bra*_*ano 2 html python django

在 view.py 中,我尝试拆分一个长字符串的属性值。在正常查询中:

someposts = Posts.object.all()[3:5]
Run Code Online (Sandbox Code Playgroud)

应该返回:

<QuerySet [object<first post>, object<second post>]>
Run Code Online (Sandbox Code Playgroud)

然后我按如下方式查询帖子,因为我需要拆分属性值(在此更改之后我收到错误):

someposts = Posts.object.all().values('id', 'title', 'tags')[3:5]
Run Code Online (Sandbox Code Playgroud)

所以它返回类似:

<QuerySet [{'id': 2, 'title': 'first post', 'tags': ' X1, X2, X3,.., X10'}, {'id': 4, 'title': 'second post', 'tags': ' S1, S2, S3,.., S8'}]
Run Code Online (Sandbox Code Playgroud)

但我希望收到tags一份叮咬清单,所以我做了什么:

splited_tags = [v['tags'].split(',') for v in someposts]
for n, i in enumerate(someposts):
        someposts[n]['tags'] = splited_tags[n]
Run Code Online (Sandbox Code Playgroud)

结果

<QuerySet [{'id': 2, 'title': 'first post', 'tags': [' X1', 'X2', 'X3',.., X10']}, {'id': 4, 'title': 'second post', 'tags': [' S1', 'S2', 'S3,.., 'S8']}]
Run Code Online (Sandbox Code Playgroud)

因为我要传递someposts给我的模板:

context = {
       'someposts':someposts,
}
return render(request, 'app/home.html', context)
Run Code Online (Sandbox Code Playgroud)

在 home.html 中:

{%for post in someposts %}
     <a class="avator" href="{% url 'user-post' post.author.username %}"></a>
{ % endfor %}
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

未找到参数 '('',)' 的反向 'user-post'

我认为问题是post.author.username因为 post 是一个字符串,它没有.author属性,所以这将被评估为 string_if_invalid ,除非我另外指定,否则它是空字符串 '' 。

你知道如何解决这个错误吗?或者如何拆分查询集中的字符串?

Bed*_*bek 6

当然,您有多种选择可以解决问题,但为了快速解决您的问题,我将为您提供最简单的解决方案:

您实际上可以对帖子进行注释,以便在每个帖子中都有作者用户名:

from django.db.models import F
Post.objects.annotate(formatted_author=F('author__username')).values('id', 'formatted_author')
Run Code Online (Sandbox Code Playgroud)

这样,您就可以获得撰写帖子的作者的用户名。但是,请小心,如果某些帖子的作者字段中有一些值,您将在响应中得到None值。


但是,如果您使用Postgres作为数据库,则可以使用Postgres的内置ArrayField功能,并通过以下方式使您的注释更加优雅:

from django.db.models.expressions import Func
from django.db.models import F, Value, TextField
from django.contrib.postgres.fields import ArrayField

formatted_author = F('author__username')
formatted_tags = Func(
    F('tags'),
    Value(","),
    function='regexp_split_to_array', output=ArrayField(TextField())
)

Post.objects \
    .annotate(formatted_author=formatted_author, formatted_tags=formatted_tags) \
    .values('id', 'formatted_author', 'formatted_tags')
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您还可以节省之前每次迭代时进行 SQL 查询的成本。


更新1

如果你没有Postgres数据库,那么我很遗憾地告诉你,你仍然需要在python代码中处理标签,并且我对你当前的实现有一些更好的重构。那么你现在拥有的是:

splited_tags = [v['tags'].split(',') for v in someposts]
for n, i in enumerate(someposts):
        someposts[n]['tags'] = splited_tags[n]
Run Code Online (Sandbox Code Playgroud)

而且,我的建议是:

someposts = list(map(lambda somepost: dict(formatted_tags=somepost['tags'].split(','), **somepost), someposts))
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它只需要一次迭代就可以进行新的属性权衡,但这只是我的建议。


更新2

当帖子所有者在评论中询问post.author.profile.user.first_name属性时,我想详细说明这一点。post.author.profile.image.url

一般来说,您无法检索这些属性,因为您没有在函数中显式选择它们.values().values()将仅检索那些选定的属性。因此,基本上您可以做的就是再次创建F带有注释的变量(为了减少名称长度):

from django.db.models import F
Post.objects.annotate(formatted_author=F('author__username'), formatted_first_name=F('author__profile__user__first_name'), formatted_image=F('author__profile__image')).values('id', 'formatted_author', 'formatted_first_name', 'formatted_image')
Run Code Online (Sandbox Code Playgroud)

正如我的直觉告诉我的那样,实际上post.author.first_name相当于post.author.profile.user.first_name,但如果你有不同的模型结构,我可能是错的。:)