首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django.db.models.deletion related_objects采用3个位置参数

Django.db.models.deletion related_objects采用3个位置参数
EN

Stack Overflow用户
提问于 2021-08-05 04:53:11
回答 1查看 41关注 0票数 0

我正在将我的项目从Django 2.2升级到3.2,并绞尽脑汁解决他们代码中似乎存在的bug。

我有一个测试,它对一个资源(顺便说一下,是一个DjangoRestFramework资源,DRF版本是3.12.4)执行一个简单的DELETE请求,在django.db.models.deletion内部发生了崩溃。下面是堆栈跟踪的相关部分:

代码语言:javascript
复制
        response = admin_client.delete(
            reverse(self.url_name, args=[project.pk, category.pk]),
>           content_type='application/json'
        )

test_category_views.py:609: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/rn/venvs/lib/python3.6/site-packages/django/test/client.py:795: in delete
    response = super().delete(path, data=data, content_type=content_type, secure=secure, **extra)
/home/rn/venvs/lib/python3.6/site-packages/django/test/client.py:447: in delete
    secure=secure, **extra)
/home/rn/venvs/lib/python3.6/site-packages/django/test/client.py:473: in generic
    return self.request(**r)
/home/rn/venvs/lib/python3.6/site-packages/django/test/client.py:719: in request
    self.check_exception(response)
/home/rn/venvs/lib/python3.6/site-packages/django/test/client.py:580: in check_exception
    raise exc_value
/home/rn/venvs/lib/python3.6/site-packages/django/core/handlers/exception.py:47: in inner
    response = get_response(request)
../../../hazardlog/platform.py:127: in _get_response
    response = self.process_exception_by_middleware(e, request)
../../../hazardlog/platform.py:125: in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
/usr/local/lib/python3.6/contextlib.py:52: in inner
    return func(*args, **kwds)
/home/rn/venvs/lib/python3.6/site-packages/django/views/decorators/csrf.py:54: in wrapped_view
    return view_func(*args, **kwargs)
/home/rn/venvs/lib/python3.6/site-packages/django/views/generic/base.py:70: in view
    return self.dispatch(request, *args, **kwargs)
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/views.py:509: in dispatch
    response = self.handle_exception(exc)
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/views.py:469: in handle_exception
    self.raise_uncaught_exception(exc)
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/views.py:480: in raise_uncaught_exception
    raise exc
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/views.py:506: in dispatch
    response = handler(request, *args, **kwargs)
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/generics.py:291: in delete
    return self.destroy(request, *args, **kwargs)
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/mixins.py:91: in destroy
    self.perform_destroy(instance)
/home/rn/venvs/lib/python3.6/site-packages/rest_framework/mixins.py:95: in perform_destroy
    instance.delete()
/home/rn/venvs/lib/python3.6/site-packages/django/db/models/base.py:953: in delete
    collector.collect([self], keep_parents=keep_parents)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.models.deletion.Collector object at 0x7f7870265ac8>
objs = [<Category: Test category 0>], source = None, nullable = False
collect_related = True, source_attr = None, reverse_dependency = False
keep_parents = False, fail_on_restricted = True

    def collect(self, objs, source=None, nullable=False, collect_related=True,
                source_attr=None, reverse_dependency=False, keep_parents=False,
                fail_on_restricted=True):
        """
        Add 'objs' to the collection of objects to be deleted as well as all
        parent instances.  'objs' must be a homogeneous iterable collection of
        model instances (e.g. a QuerySet).  If 'collect_related' is True,
        related objects will be handled by their respective on_delete handler.
    
        If the call is the result of a cascade, 'source' should be the model
        that caused it and 'nullable' should be set to True, if the relation
        can be null.
    
        If 'reverse_dependency' is True, 'source' will be deleted before the
        current model, rather than after. (Needed for cascading to parent
        models, the one case in which the cascade follows the forwards
        direction of an FK rather than the reverse direction.)
    
        If 'keep_parents' is True, data of parent model's will be not deleted.
    
        If 'fail_on_restricted' is False, error won't be raised even if it's
        prohibited to delete such objects due to RESTRICT, that defers
        restricted object checking in recursive calls where the top-level call
        may need to collect more objects to determine whether restricted ones
        can be deleted.
        """
        if self.can_fast_delete(objs):
            self.fast_deletes.append(objs)
            return
        new_objs = self.add(objs, source, nullable,
                            reverse_dependency=reverse_dependency)
        if not new_objs:
            return
    
        model = new_objs[0].__class__
    
        if not keep_parents:
            # Recursively collect concrete model's parent models, but not their
            # related objects. These will be found by meta.get_fields()
            concrete_model = model._meta.concrete_model
            for ptr in concrete_model._meta.parents.values():
                if ptr:
                    parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
                    self.collect(parent_objs, source=model,
                                 source_attr=ptr.remote_field.related_name,
                                 collect_related=False,
                                 reverse_dependency=True,
                                 fail_on_restricted=False)
        if not collect_related:
            return
    
        if keep_parents:
            parents = set(model._meta.get_parent_list())
        model_fast_deletes = defaultdict(list)
        protected_objects = defaultdict(list)
        for related in get_candidate_relations_to_delete(model._meta):
            # Preserve parent reverse relationships if keep_parents=True.
            if keep_parents and related.model in parents:
                continue
            field = related.field
            if field.remote_field.on_delete == DO_NOTHING:
                continue
            related_model = related.related_model
            if self.can_fast_delete(related_model, from_field=field):
                model_fast_deletes[related_model].append(field)
                continue
            batches = self.get_del_batches(new_objs, [field])
            for batch in batches:
>               sub_objs = self.related_objects(related_model, [field], batch)
E               TypeError: related_objects() takes 3 positional arguments but 4 were given

/home/rn/venvs/ramrisk/lib/python3.6/site-packages/django/db/models/deletion.py:282: TypeError

因此,related_objects使用4个位置参数调用,尽管它应该只接受3。Python使用这些参数计算self,所以4是正确的。它被selfrelated_model[field]batch调用。太棒了。那么,让我们看看django.db.models.deletion中self.related_objects的定义:

代码语言:javascript
复制
    def related_objects(self, related_model, related_fields, objs):
        """
        Get a QuerySet of the related model to objs via related fields.
        """
        predicate = reduce(operator.or_, (
            query_utils.Q(**{'%s__in' % related_field.name: objs})
            for related_field in related_fields
        ))
        return related_model._base_manager.using(self.using).filter(predicate)

好吧,很明显,这需要4个参数,那么这个TypeError是从哪里来的呢?请记住,我只展示了Django 3.2代码,没有我自己的代码。即使我以某种方式在这些变量中加入了一些疯狂的东西,它们也永远不会产生那个错误……

EN

回答 1

Stack Overflow用户

发布于 2021-08-05 06:09:14

好了,找到我的答案了。实际上,这可能是没有人能够猜到的事情,但我只想分享我学到的东西。

所以我是对的,这个错误没有意义,因为它不符合函数签名。这应该永远不会发生。那么如何调试呢?

嗯,我的第一个直觉是,也许这个函数定义在运行时的某个地方被替换了。我该怎么检查呢?我在调用该方法的行上设置了一个断点,然后在我的调试器中

代码语言:javascript
复制
import inspect
inspect.getmodule(self.related_objects)

这给了我答案。看起来我们已经修补了这个函数,用额外的功能来装饰它,但是现在Django版本升级了,预期的签名也改变了。

这就是为什么Monkey补丁是危险的,不能随意使用。这里的教训:如果你做的是猴子补丁,那么每次升级lib版本时,一定要重新访问所有这些补丁。这一次失败了,所以我必须找出发生了什么,但它很容易就会变得更加险恶。它可能会默默地做一些不同和不正确的事情,因为库有一些小的变化,而猴子补丁并没有意识到这一点。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68660787

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档