Python 属性错误:无法设置属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27396339/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 01:45:33  来源:igfitidea点击:

AttributeError: can't set attribute

pythondjangounit-testinghttpresponse

提问by austiine

I am working on a legacy django project, in there somewhere there is a class defined as follows;

我正在处理一个遗留的 django 项目,在某个地方有一个定义如下的类;

from django.http import HttpResponse

class Response(HttpResponse):
    def __init__(self, template='', calling_context='' status=None):
        self.template = template
        self.calling_context = calling_context
        HttpResponse.__init__(self, get_template(template).render(calling_context), status)

and this class is used in views as follows

并且这个类在视图中使用如下

def some_view(request):
    #do some stuff
    return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))

this class was mainly created so that they could use it to perform assertions in the unit tests .i.e they are not using django.test.Client to test the views but rather they create a mock request and pass that to view as(calling the view as a callable) in the tests as follows

创建此类主要是为了他们可以使用它在单元测试中执行断言。即他们不使用 django.test.Client 来测试视图,而是创建一个模拟请求并将其传递给视图(调用视图作为可调用的)在测试中如下

def test_for_some_view(self):
    mock_request = create_a_mock_request()
    #call the view, as a function
    response = some_view(mock_request) #returns an instance of the response class above
    self.assertEquals('some_template.html', response.template)
    self.assertEquals({}, response.context)

The problem is that half way through the test suite(quite a huge test suite), some tests begin blowing up when executing the

问题是在测试套件(相当大的测试套件)进行到一半时,一些测试在执行时开始爆炸

return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))

and the stack trace is

和堆栈跟踪是

self.template = template
AttributeError: can't set attribute 

the full stack trace looks something like

完整的堆栈跟踪看起来像

======================================================================
ERROR: test_should_list_all_users_for_that_specific_sales_office
 ----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/austiine/Projects/mped/console/metrics/tests/unit/views/sales_office_views_test.py",   line 106, in test_should_list_all_users_for_that_specific_sales_office
    response = show(request, sales_office_id=sales_office.id)
File "/Users/austiine/Projects/mped/console/metrics/views/sales_office_views.py", line 63, in show
    "sales_office_users": sales_office_users}))
File "/Users/austiine/Projects/mped/console/metrics/utils/response.py", line 9, in __init__
    self.template = template
    AttributeError: can't set attribute

the actual failing test is

实际失败的测试是

def test_should_list_all_users_for_that_specific_sales_office(self):
    user_company = CompanyFactory.create()
    request = self.mock_request(user_company)
    #some other stuff

    #calling the view
    response = show(request, sales_office_id=sales_office.id)
    self.assertIn(user, response.calling_context["sales_office_users"])
    self.assertNotIn(user2, response.calling_context["sales_office_users"])

code for the show view

显示视图的代码

def show(request, sales_office_id):
    user = request.user
    sales_office = []
    sales_office_users = []
    associated_market_names = []
    try:
        sales_office = SalesOffice.objects.get(id=sales_office_id)
        sales_office_users = User.objects.filter(userprofile__sales_office=sales_office)
        associated_market_names = Market.objects.filter(id__in=           (sales_office.associated_markets.all())).values_list("name", flat=True)
        if user.groups.all()[0].name == UserProfile.COMPANY_AO:
            associated_market_names = [market.name for market in sales_office.get_sales_office_user_specific_markets(user)]
        except:
            pass
    return Response("sales_office/show.html", RequestContext(request, {'keys': 'values'}))

回答by Stephen Lin

It looks like you don't use self.templatein Responseclass. Try like this:

它看起来像你不使用self.templateResponse类。像这样尝试:

class Response(HttpResponse):
    def __init__(self, template='', calling_context='' status=None):
        HttpResponse.__init__(self, get_template(template).render(calling_context), status)

回答by Michele d'Amico

I took a look to django source code I've no idea where templateor templatesattribute come from in HttpResponse. But I can propose to you to change your test approach and migrate to mockframework. You can rewrite your test like:

我接过来一看,以Django的源代码,我不知道在哪里templatetemplates属性来自于HttpResponse。但是我可以建议您更改测试方法并迁移到模拟框架。您可以像这样重写测试:

@patch("qualified_path_of_response_module.response.Response", spec=Response)
def test_should_list_all_users_for_that_specific_sales_office(self,mock_resp):
    user_company = CompanyFactory.create()
    request = self.mock_request(user_company)
    #some other stuff

    #calling the view
    response = show(request, sales_office_id=sales_office.id)
    self.assertTrue(mock_resp.called)
    context = mock_resp.call_args[0][2]
    self.assertIn(user, context["sales_office_users"])
    self.assertNotIn(user2, context["sales_office_users"])

@patchdecorator replace your Response()class by a MagicMock()and pass it to your test method as mock_respvariable. You can also use patchas context manager by withconstruct but decorators are the cleaner way to do it. I don't know if Responseis just a stub class for testing but in that case you can patch directly HttpResponce, but it depends from your code.

@patch装饰器用 a 替换您的Response()MagicMock()并将其作为mock_resp变量传递给您的测试方法。您也可以patch通过with构造将其用作上下文管理器,但装饰器是一种更简洁的方法。我不知道它Response是否只是用于测试的存根类,但在这种情况下,您可以直接修补HttpResponce,但这取决于您的代码。

You can find details about call_argshere. Maybe you need to use specattribute because django make some type checking... but try with and without it (I'm not a django expert). Explore mockframework: it'll give to you lot of powerful tools to make simple tests.

您可以call_args在此处找到有关详细信息。也许您需要使用spec属性,因为 django 会进行一些类型检查……但是尝试使用和不使用它(我不是 django 专家)。探索mock框架:它将为您提供许多强大的工具来进行简单的测试。

回答by yoniLavi

This answer doesn't address the specifics of this question, but explains the underlying issue. This specific exception "AttributeError: can't set attribute" is raised (see source) when the attribute you're attempting to change is actually a propertythat doesn't have a setter. If you have access to the library's code, adding a setter would solve the problem.

这个答案没有解决这个问题的细节,而是解释了潜在的问题。当您尝试更改的属性实际上是没有 setter的属性时,会引发此特定异常“AttributeError: can't set attribute”(请参阅源代码)。如果您可以访问库的代码,添加一个 setter 可以解决问题。

EDIT: updated source link to new location in the code.

编辑:更新了代码中新位置的源链接。