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
AttributeError: can't set attribute
提问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.template
in Response
class. Try like this:
它看起来像你不使用self.template
的Response
类。像这样尝试:
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 template
or templates
attribute 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的源代码,我不知道在哪里template
或templates
属性来自于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"])
@patch
decorator replace your Response()
class by a MagicMock()
and pass it to your test method as mock_resp
variable. You can also use patch
as context manager by with
construct but decorators are the cleaner way to do it. I don't know if Response
is 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_args
here. Maybe you need to use spec
attribute because django make some type checking... but try with and without it (I'm not a django expert). Explore mock
framework: 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.
编辑:更新了代码中新位置的源链接。