我正在使用django-activity-stream来显示最近发生的事件列表。为了举例,这些可能是有人评论或有人编辑文章。即GenericForeignKey action_object
可以引用Comment
或Article
。我想显示指向action_object
的链接:
<a href="{{ action.action_object.get_absolute_url }}">
{{ action.action_object }}
</a>
问题是这会导致对每个项目的查询,特别是Comment.get_absolute_url
要求评论的article
尚未提取,并且Article.__unicode__
要求其revision.content
,这也没有得到。
django-activity-stream
已自动调用prefetch_related('action_object')
(related discussion)。
虽然the docs说{{ action.action_object.id }}
,但action_object_content_type
prefetch_related
只会对query = query.prefetch_related('action_object__article`, `action_object__revision`)
进行单一查询,但这似乎正在进行测试。
它还支持预取GenericRelation和GenericForeignKey,但是,它必须限制为一组同类结果。例如,仅当查询限制为一个ContentType时,才支持预取GenericForeignKey引用的对象。
并且有多种内容类型。但是在上面的用例中,我需要额外的Article
次调用,例如:
__article
但是这会抱怨,因为Comment
没有__revision
(并且可能会抱怨comments = query._clone().filter(action_object_content_type=comment_ctype).prefetch_related('action_object__article')
articles = query._clone().filter(action_object_content_type=article_ctype).prefetch_related('action_object__revision')
query = comments | articles
如果距离那么prefetch_related
也没有projects' for nil:NilClass):
。我假设这是文档真正指的是什么。所以我想我会试试这个:
app/controllers/project_controller.rb:8:in
before_action :confirm_logged_in
before_action :find_company
def index
@projects = @company.projects.sorted
end
def show
@project = Project.find(params[:id])
end
def new
@project = Project.new()
end
def create
@project = Project.new(project_params)
if @project.save
redirect_to(:action => 'index')
else
render('new')
end
end
def edit
@project = Project.find(params[:id])
end
def update
@project = Project.find(params[:id])
if @project.update_attributes(project_params)
redirect_to(:action => 'index')
else
render('new')
end
end
def delete
@project = Project.find(params[:id])
end
def destory
@project = Project.find(params[:id])
@project.destroy
redirect_to(:action => 'index')
end
private
def project_params
params.require(:project).permit(:name, :position, :type_of_project, :description, :no_of_tasks)
end
def find_company
if params[:company_id]
@company = Company.find(params[:company_id])
end
end
end
但结果总是空的。我猜查询集只支持单个CurrentSheet.Cells(PRow, "AD").FormulaR1C1 = "=CONCATENATE(YEAR(R[0]C[-19]), ""/"",TEXT(MONTH(R[0]C[-19]),""MM""))"
列表,不能像那样加入。
我喜欢返回一个查询集,因为稍后会在代码中进行进一步的过滤,而这部分不知道。虽然一旦查询集最终被评估,我希望能够让django获取呈现事件所需的一切。
还有其他办法吗?
我看了Prefetch
objects,但我不认为他们在这种情况下提供任何帮助。
答案 0 :(得分:1)
可以在django-notify-x
中找到一个解决方案,该解决方案来自django-notifications
,而django-activity-stream
来自li
。它使用了一个&#34; django片段&#34;链接在下面的复制文本中。
https://github.com/v1k45/django-notify-x/pull/19
使用https://djangosnippets.org/snippets/2492/中的代码段, 预取通用关系以减少查询次数。
目前,我们为每个通用关系触发一个额外的查询 对于每个记录,使用此代码,我们减少一个额外的查询 每种类型的通用关系的每个通用关系。
如果您的所有通知都与徽章模型相关,则只有一个 将触发aditional查询。
答案 1 :(得分:1)
对于Django 1.10和1.11,我使用上面修改的片段如下(以防万一你没有使用django-activity-stream):
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields as generic
def get_field_by_name(meta, fname):
return [f for f in meta.get_fields() if f.name == fname]
def prefetch_relations(weak_queryset):
weak_queryset = weak_queryset.select_related()
# reverse model's generic foreign keys into a dict:
# { 'field_name': generic.GenericForeignKey instance, ... }
gfks = {}
for name, gfk in weak_queryset.model.__dict__.items():
if not isinstance(gfk, generic.GenericForeignKey):
continue
gfks[name] = gfk
data = {}
for weak_model in weak_queryset:
for gfk_name, gfk_field in gfks.items():
related_content_type_id = getattr(weak_model, get_field_by_name(gfk_field.model._meta, gfk_field.ct_field)[
0].get_attname())
if not related_content_type_id:
continue
related_content_type = ContentType.objects.get_for_id(related_content_type_id)
related_object_id = int(getattr(weak_model, gfk_field.fk_field))
if related_content_type not in data.keys():
data[related_content_type] = []
data[related_content_type].append(related_object_id)
for content_type, object_ids in data.items():
model_class = content_type.model_class()
models = prefetch_relations(model_class.objects.filter(pk__in=object_ids))
for model in models:
for weak_model in weak_queryset:
for gfk_name, gfk_field in gfks.items():
related_content_type_id = getattr(weak_model,
get_field_by_name(gfk_field.model._meta, gfk_field.ct_field)[
0].get_attname())
if not related_content_type_id:
continue
related_content_type = ContentType.objects.get_for_id(related_content_type_id)
related_object_id = int(getattr(weak_model, gfk_field.fk_field))
if related_object_id != model.pk:
continue
if related_content_type != content_type:
continue
setattr(weak_model, gfk_name, model)
return weak_queryset
这给了我预期的结果。
编辑:
要使用它,只需调用prefetch_relations,并将QuerySet作为参数。
例如,而不是:
my_objects = MyModel.objects.all()
你可以这样做:
my_objects = prefetch_relations(MyModel.objects.all())