gpt4 book ai didi

django - django-treebeard 物化路径节点的动态排序

转载 作者:行者123 更新时间:2023-12-05 05:15:57 30 4
gpt4 key购买 nike

我有一个基于 django-oscar(和 django-cms)的项目,它在多个域上运行,使用不同的 SITE_ID django.contrib.sites 模块。该项目已经富有成效,我不能再更改类别 slugs - 我希望避免将整个代码切换到嵌套集树或相邻树 - 为了您更好地理解:初始要求不需要不同的类别排序对于每个域,所以我只使用 Oscars 默认类别实现。

但由于奥斯卡类别模型/经理是基于django-treebeardmaterialized path tree实现,我必须考虑与更改默认顺序的常用 Django 方式的一些差异。

鉴于模型中的这两个额外字段(继承自 django-oscars 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']

首先,它将忽略此指令,因为 treebeardMP_NodeManager.get_queryset() 不遵守自定义排序 - 对于 MP_Tree 逻辑,它必须依赖生成的排序在插入时(存储在 path 中),因此这种方法违背了 MP_Tree 本身的目的。

我还查看了 node_order_by - 但如 docs 中所述:

Incorrect ordering of nodes when node_order_by is enabled. Ordering is enforced on node insertion, so if an attribute in node_order_by is modified after the node is inserted, the tree ordering will be inconsistent.

但是对于具有不同类别排序的多个域,这同样是没有用的。我能想到的唯一方法是重写 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 语句链中反射(reflect)这种逻辑,以便我可以继续使用 native 查询集?

最佳答案

所以这个问题一开始让我很头疼——我尝试了不同的方法,但没有一个是令人满意的——尝试制作一个动态排序的 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=产品.first_category %}

管理员

您需要对 apps.dashboard 中的 View 、表单和表单集进行更改,以处理新添加的 django_sitesite_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')

关于django - django-treebeard 物化路径节点的动态排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51027292/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com