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

时间:2018-06-25 15:42:32

标签: django django-oscar django-treebeard

我有一个基于django-oscar(和django-cms)的项目,该项目使用SITE_ID模块在​​具有不同django.contrib.sites的多个域上运行。该项目已经非常有效,我不能再更改类别标签了-我也希望避免将整个代码切换到“嵌套集树”或“相邻树”中-为了更好地理解:最初的需求并不想进行其他类别排序对于每个域,因此我只使用了Oscars默认类别实现。

但是由于Oscars类别模型/管理器基于django-treebeard的{​​{3}}实现,因此我不得不考虑与更改默认顺序的常规Django方法的一些区别。

鉴于模型中的这两个额外字段(继承自django-oscar的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的{​​{1}}不遵守自定义排序-对于MP_Tree逻辑,它必须依赖在插入时生成的排序(存储在{{ 1}}),因此这种方法会违背MP_Tree本身的目的。

我也看过MP_NodeManager.get_queryset()-但如materialized path tree所述:

  

启用path时,节点的顺序不正确。订购是   在节点插入时强制执行,因此如果node_order_by中的属性为   插入节点后修改,树的顺序将是   不一致。

但是同样在具有不同类别顺序的多个域上,这没有用。我唯一能想到的就是重写node_order_by类:

node_order_by

但是这种方法显然是错误的,因为MP_Tree依靠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') ,忽略最后一部分并进行排序根据{{​​1}}并将其重新转换为path

我什至不确定是否可行,我想避免去那里,所以我的问题是:有没有办法在ORM语句链中反映这种逻辑,以便我可以继续使用本机path

1 个答案:

答案 0 :(得分:1)

所以这个问题一开始让我非常头疼-我尝试了不同的方法,但没有一个方法令人满意-尝试制作动态排序的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_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')