在Django 1.3中合并重复项的最佳方式是什么?

2 投票
2 回答
768 浏览
提问于 2025-04-17 10:13

我找到了一段代码,链接在这里 http://djangosnippets.org/snippets/2283/,但是我觉得它在处理多对多字段时不太好用,尤其是当这些字段涉及到另一个模型(通过B连接到C)时。我在尝试合并的时候遇到了一个属性错误。

你知道有什么办法可以解决这个问题,或者有没有其他合并对象的方法吗?

编辑:更多细节

我有三个模型:A、B、C。

A有一个多对多字段“m2mfield”,它通过B指向C。

当我运行Django代码片段时,它出现了异常

'ManyRelatedManager' object has no attribute 'remove'

我觉得这和Django源代码中的一条注释有关(django.db.models.fields.related.py 第499行),内容是:

# If the ManyToMany relation has an intermediary model,
# the add and remove methods do not exist.

我认为我找到的代码片段没有区分有中介模型和没有中介模型的多对多关系。这就是我想找办法修复这段代码,或者找到其他实现我想要的(合并)的方法的原因。

2 个回答

0

你遇到的这个错误是因为你想要填充的多对多字段是通过一个中间模型来管理的。

在你粘贴的代码片段中,有一段代码应该是了解中间模型的,这样合并才会有效,它从第55行开始:

    # Migrate all many to many references from alias object to primary object.
    for related_many_object in alias_object._meta.get_all_related_many_to_many_objects():
        alias_varname = related_many_object.get_accessor_name()
        obj_varname = related_many_object.field.name

        if alias_varname is not None:
            # standard case
            related_many_objects = getattr(alias_object, alias_varname).all()
        else:
            # special case, symmetrical relation, no reverse accessor
            related_many_objects = getattr(alias_object, obj_varname).all()
        for obj in related_many_objects.all():
            getattr(obj, obj_varname).remove(alias_object)
            getattr(obj, obj_varname).add(primary_object)  # this can't work

你需要给merge_model_objects提供一个函数字典,这样merge_model_objects才能选择一个函数来构建中间类。很可能这段代码应该替换我上面提到的代码片段的最后一行。

不过,你还需要注意,A1、A2和C1、C2可能是相等的,而B1、B2却不相等,这一点当前的代码也没有处理。

2

我最终修改了代码,以处理通过一个模型创建的 ManyToMany 字段的情况。以下是需要修改的部分:

# Migrate all many to many references from alias object to primary object.
for related_many_object in alias_object._meta.get_all_related_many_to_many_objects():
    alias_varname = related_many_object.get_accessor_name()
    obj_varname = related_many_object.field.name

    # Treatment depends on if the many_to_many field is created through another model
    if getattr(alias_object, alias_varname).through._meta.auto_created:
        if alias_varname is not None:
            # standard case
            related_many_objects = getattr(alias_object, alias_varname).all()
        else:
            # special case, symmetrical relation, no reverse accessor
            related_many_objects = getattr(alias_object, obj_varname).all()
        for obj in related_many_objects.all():
            getattr(obj, obj_varname).remove(alias_object)
            getattr(obj, obj_varname).add(primary_object)
    else:
        related_many_objects = getattr(alias_object, alias_varname).all()

        through_model = getattr(alias_object, alias_varname).through
        through_field_name = None
        for f in through_model._meta.fields:
            if isinstance(f, ForeignKey):
                if f.rel.to == primary_class :
                # f is the field in our 'through' model which points to an instance of primary_class
                    through_field_name = f.name

        for obj in related_many_objects.all():
            kwargs = {
                through_field_name: obj,
            }
            for through_obj in through_model.objects.filter(**kwargs):
                setattr(through_obj, through_field_name, primary_object)
                through_obj.save()

撰写回答