django - django-treebeard物化路径节点的动态排序
问题描述
我有一个基于django-oscar
(and ) 的项目,它使用该模块django-cms
在具有不同SITE_ID
s 的多个域上运行。django.contrib.sites
该项目已经富有成效,我不能再更改类别 slug - 我希望避免将整个代码切换到嵌套集树或相邻树 - 为了您更好地理解:初始要求不想要不同的类别排序对于每个域,所以我只使用 Oscars 默认类别实现。
但是由于 Oscars 类别模型/管理器基于物化路径树实现,因此我必须考虑与通常的 Django 更改默认排序方式的一些差异django-treebeard
。
给定模型中的这两个额外字段(继承自django-oscar
s AbstractCategory)
class Category(AbstractCategory):
# ...
order_site_1 = models.IntegerField(
null=False,
default=0
)
order_site_2 = models.IntegerField(
null=False,
default=0
)
我不能简单地将排序添加到元类中,例如:
class Category(AbstractCategory):
class Meta:
ordering = ['depth', '-order_site_{}'.format(Site.objects.get_current().id), 'name']
首先,它将忽略该指令,因为treebeard
sMP_NodeManager.get_queryset()
不尊重自定义排序 - 对于 MP_Tree 逻辑,它必须依赖于插入时生成的排序(存储在 中path
),因此这种方法将违背 MP_Tree 本身的目的。
我也看过node_order_by
- 但如文档中所述:
node_order_by
启用时节点的顺序不正确。在节点插入时强制执行排序,因此如果在node_order_by
插入节点后修改了一个属性,则树排序将不一致。
但同样在具有不同类别排序的多个域上,这是没有用的。我能想到的唯一方法是覆盖MP_NodeManager
类:
class CustomOrderQuerySet(MP_NodeManager):
def get_queryset(self):
sort_key = '-order_site_{}'.format(Site.objects.get_current().id)
return MP_NodeQuerySet(self.model).order_by('depth', sort_key, 'name')
但是这种方法显然是错误的,因为 MP_Tree 依赖于path
排序来生成正确的树 - 我的想法是遍历所有条目,比较path
,忽略最后一部分并根据排序order_site_{id}
并将其重新转换为QuerySet
.
我什至不确定这是否可能,并且我想避免去那里,所以我的问题是:有没有办法在 ORM 语句链中反映这种逻辑,以便我可以继续使用本机QuerySet
?
解决方案
所以这个问题一开始让我很头疼——我尝试了不同的方法,但没有一个是令人满意的——尝试制作一个动态排序的 MP_Tree 是我在这一点上非常不鼓励的事情,它太混乱了。最好坚持在插入时创建的排序并在该阶段处理所有变量。我还要感谢@solarissmoke 在评论中提供的帮助。
楷模
class Product(AbstractProduct):
# ...
@property
def site_categories(self):
return self.categories.filter(django_site_id=settings.SITE_ID)
@property
def first_category(self):
if not self.site_categories:
return None
return self.site_categories[0]
class Category(AbstractCategory):
# ...
django_site = models.ForeignKey(Site, null=False)
site_rank = models.IntegerField(_('Site Rank'), null=False, default=0)
node_order_by = ['django_site_id', 'site_rank', 'name']
模板标签
复制templatetags/category_tags#get_annotated_list
到项目并稍作修改:
# ...
start_depth, prev_depth = (None, None)
if parent:
categories = parent.get_descendants()
if max_depth is not None:
max_depth += parent.get_depth()
else:
categories = Category.get_tree()
# Just add this line
categories = categories.filter(django_site_id=settings.SITE_ID)
# ...
模板
对于templates/catalogue/detail.html
:替换{% with category=product.categories.all.0 %}
为{% with category=product.first_category %}
行政
您将需要对其中的视图、表单和表单集进行更改apps.dashboard
以处理新添加的内容django_site
-site_rank
我忽略了这一点,因为在我的情况下,所有类别都是通过定期导入的 CSV 定义的。这应该没什么大不了的 - 也许稍后我会这样做并更新这个答案。
创建 MP 节点
每当您添加根节点、子节点或兄弟节点时,您现在都需要传入django_site_id
参数(以及包含在 中的所有其他必需值node_order_by
),例如:
parent_category.add_child(
django_site_id=settings.SITE_ID, site_rank=10, name='Child 1')
Category.add_root(
django_site_id=settings.SITE_ID, site_rank=1, name='Root 1')
推荐阅读
- docusignapi - 从表单中读取电子邮件 ID 并以编程方式更新信封中的收件人列表
- azure - Azure Durable Functions - OrchestrationTrigger 卡住
- vue.js - Vuemapbox 标记显示在页面底部
- c++ - 如何使用 CMake 和 Qt Creator 自动将 Qt 依赖项复制到构建目录?
- flutter - 如何在 Flutter 中检查 ShareExtend.share() 的目的地
- azure - 如何获取 Azure 数据工厂数据集、触发器、管道和链接服务的数量?
- php - 将 WordPress 帖子添加到 Elementor Pro 表单选择字段
- subshell - 子shell中奇怪的ksh93行为
- c# - 有没有办法访问 Roslyn 中降低的 C# AST
- google-data-studio - Vega-Lite:堆叠分组条形图