Python / Django Rest Framework 奇怪的错误仅在使用调试器时发生
Python / Django Rest Framework weird error happening only when using debugger
这个问题在仔细研究之后已经解决了。请阅读下面我的回答。
当 运行 我的 Django 应用程序时,我得到 "The fields
option must be a list or tuple. Got str."。
运行和调试器一模一样的代码,如果我在报错的那行打了断点,那也不会失败,应该是元组的好像是元组。
问题似乎出在 DRF ModelSerializer 中的以下代码中:
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(ChHiveLevel1Serializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop fields that are specified in the `fields` argument.
for field_name in fields:
self.fields.pop(field_name)
print("fields to be included: ", self.fields)
在 views.py 我只是这样做:
...
hives = profile.hive_subscriptions
# En fields se le pasa el campo a eliminar del serializador
fields = ('priority', )
serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True)
...
这是追溯:
Internal Server Error: /profiles/diegoocampo8/hives/
Traceback (most recent call last):
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/django/core/handlers/base.py", line 111, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
return view_func(*args, **kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/django/views/generic/base.py", line 69, in view
return self.dispatch(request, *args, **kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/views.py", line 452, in dispatch
response = self.handle_exception(exc)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/views.py", line 449, in dispatch
response = handler(request, *args, **kwargs)
File "/home/diego/PycharmProjects/chattyhive/API/views.py", line 271, in get
serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 96, in __new__
return cls.many_init(*args, **kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 116, in many_init
child_serializer = cls(*args, **kwargs)
File "/home/diego/PycharmProjects/chattyhive/API/serializers.py", line 274, in __init__
print("fields to be included: ", self.fields)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/utils/serializer_helpers.py", line 120, in __repr__
return dict.__repr__(self.fields)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 611, in __repr__
return unicode_to_repr(representation.list_repr(self, indent=1))
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/utils/representation.py", line 97, in list_repr
if hasattr(child, 'fields'):
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 313, in fields
for key, value in self.get_fields().items():
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 837, in get_fields
field_names = self.get_field_names(declared_fields, info)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 889, in get_field_names
type(fields).__name__
TypeError: The `fields` option must be a list or tuple. Got str.
[05/May/2015 17:30:34] "GET /profiles/diegoocampo8/hives/ HTTP/1.1" 500 136024
如果我删除 print("fields to be included: ", self.fields)
然后我得到了同样的错误,但它将指向 views.py
中的行 serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True)
好吧,我仍然是 Python 的菜鸟,我可能会做一些非常错误的事情,但我不明白的是为什么如果我在刚才提到的打印中插入一个断点,然后我在调试时执行相同的 api 请求,然后代码正常工作:我得到了我想要的响应,并且它没有给出任何错误(如果我删除断点它会再次给出即使使用调试器启动也会出错)。
你们知道哪里出了问题吗?非常感谢。如果需要,请向我询问任何额外信息!
编辑:进一步解释:
整个序列化器是这样的:
class ChHiveLevel1Serializer(serializers.ModelSerializer):
"""Used by the following API methods: GET hive list,
"""
category = serializers.SlugRelatedField(read_only=True, slug_field='code')
languages = serializers.SlugRelatedField(source='_languages', many=True, read_only=True, slug_field='language')
# If in the POST we only need to establish the relationship with User model (not update the model itself) we
# set read_only to True
creator = serializers.SlugRelatedField(read_only=True, slug_field='public_name')
tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='tag')
public_chat = ChPublicChatLevel1Serializer(many=False, read_only=True)
community_public_chats = ChCommunityPublicChatLevel1Serializer(many=True, read_only=True)
subscribed_users_count = serializers.IntegerField(source='get_subscribed_users_count', read_only=True)
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(ChHiveLevel1Serializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop fields that are specified in the `fields` argument.
for field_name in fields:
self.fields.pop(field_name)
print("fields to be included: ", self.fields)
class Meta:
model = ChHive
fields = ('name', 'slug', 'description', 'category', 'languages', 'creator', 'creation_date', 'tags',
'priority', 'type', 'public_chat', 'community_public_chats', 'subscribed_users_count')
我知道有 3 个不同的 'fields' 会令人困惑,所以我澄清一下:
在我将参数 'fields' 传递给序列化程序的视图中,这是一个包含要从序列化程序中动态删除的字段名称的元组。然后在 init 中取出这个参数(因此它不会发送到超类)并将其分配给本地元组 'fields'。最后,在 'if fields is not None' 中,我从 self.fields(这些是序列化程序中定义的字段)中删除名称与本地元组中的字段匹配的字段。我希望我现在能解释得更好。
这是一个视频,展示了调试和在断点处停止时如何正常工作:http://youtu.be/RImEMebBGLY
解决方案:首先,在我看来,pydevd 或 Django Rest Framework 存在一个错误,这使得我的代码在调试模式下工作并在断点处停止(在视频中显示)。但我不是 Django 专家/Python 所以这可能是预期的行为。
如上面的代码所示,ChHiveLevel1Serializer 序列化程序具有嵌套的序列化程序,例如 ChPublicChatLevel1Serializer 就是其中之一。让我们看看这个序列化器的样子:
class ChCommunityPublicChatListLevel1Serializer(serializers.ModelSerializer):
"""Used by the following API methods: GET hive list,
"""
chat = serializers.SlugRelatedField(read_only=True, slug_field='chat_id', allow_null=True)
class Meta:
model = ChCommunityPublicChat
fields = ('chat')
如错误所述,字段被定义为字符串而不是元组。正确的形式是:
class ChCommunityPublicChatListLevel1Serializer(serializers.ModelSerializer):
"""Used by the following API methods: GET hive list,
"""
chat = serializers.SlugRelatedField(read_only=True, slug_field='chat_id', allow_null=True)
class Meta:
model = ChCommunityPublicChat
fields = ('chat', )
虽然我同意我在定义这个元组时犯了一个错误,但我仍然不明白为什么将调试器设置为 ON,它就可以正常工作。 (就像使用调试器并在断点处停止一样,它突然将 ('chat') 解释为元组而不是字符串)。
这个问题在仔细研究之后已经解决了。请阅读下面我的回答。
当 运行 我的 Django 应用程序时,我得到 "The fields
option must be a list or tuple. Got str."。
运行和调试器一模一样的代码,如果我在报错的那行打了断点,那也不会失败,应该是元组的好像是元组。
问题似乎出在 DRF ModelSerializer 中的以下代码中:
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(ChHiveLevel1Serializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop fields that are specified in the `fields` argument.
for field_name in fields:
self.fields.pop(field_name)
print("fields to be included: ", self.fields)
在 views.py 我只是这样做: ...
hives = profile.hive_subscriptions
# En fields se le pasa el campo a eliminar del serializador
fields = ('priority', )
serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True)
...
这是追溯:
Internal Server Error: /profiles/diegoocampo8/hives/
Traceback (most recent call last):
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/django/core/handlers/base.py", line 111, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
return view_func(*args, **kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/django/views/generic/base.py", line 69, in view
return self.dispatch(request, *args, **kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/views.py", line 452, in dispatch
response = self.handle_exception(exc)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/views.py", line 449, in dispatch
response = handler(request, *args, **kwargs)
File "/home/diego/PycharmProjects/chattyhive/API/views.py", line 271, in get
serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 96, in __new__
return cls.many_init(*args, **kwargs)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 116, in many_init
child_serializer = cls(*args, **kwargs)
File "/home/diego/PycharmProjects/chattyhive/API/serializers.py", line 274, in __init__
print("fields to be included: ", self.fields)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/utils/serializer_helpers.py", line 120, in __repr__
return dict.__repr__(self.fields)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 611, in __repr__
return unicode_to_repr(representation.list_repr(self, indent=1))
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/utils/representation.py", line 97, in list_repr
if hasattr(child, 'fields'):
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 313, in fields
for key, value in self.get_fields().items():
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 837, in get_fields
field_names = self.get_field_names(declared_fields, info)
File "/home/diego/virtualenvs/chattyhive3.3.4/lib/python3.3/site-packages/rest_framework/serializers.py", line 889, in get_field_names
type(fields).__name__
TypeError: The `fields` option must be a list or tuple. Got str.
[05/May/2015 17:30:34] "GET /profiles/diegoocampo8/hives/ HTTP/1.1" 500 136024
如果我删除 print("fields to be included: ", self.fields)
然后我得到了同样的错误,但它将指向 views.py
serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True)
好吧,我仍然是 Python 的菜鸟,我可能会做一些非常错误的事情,但我不明白的是为什么如果我在刚才提到的打印中插入一个断点,然后我在调试时执行相同的 api 请求,然后代码正常工作:我得到了我想要的响应,并且它没有给出任何错误(如果我删除断点它会再次给出即使使用调试器启动也会出错)。
你们知道哪里出了问题吗?非常感谢。如果需要,请向我询问任何额外信息!
编辑:进一步解释:
整个序列化器是这样的:
class ChHiveLevel1Serializer(serializers.ModelSerializer):
"""Used by the following API methods: GET hive list,
"""
category = serializers.SlugRelatedField(read_only=True, slug_field='code')
languages = serializers.SlugRelatedField(source='_languages', many=True, read_only=True, slug_field='language')
# If in the POST we only need to establish the relationship with User model (not update the model itself) we
# set read_only to True
creator = serializers.SlugRelatedField(read_only=True, slug_field='public_name')
tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='tag')
public_chat = ChPublicChatLevel1Serializer(many=False, read_only=True)
community_public_chats = ChCommunityPublicChatLevel1Serializer(many=True, read_only=True)
subscribed_users_count = serializers.IntegerField(source='get_subscribed_users_count', read_only=True)
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(ChHiveLevel1Serializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop fields that are specified in the `fields` argument.
for field_name in fields:
self.fields.pop(field_name)
print("fields to be included: ", self.fields)
class Meta:
model = ChHive
fields = ('name', 'slug', 'description', 'category', 'languages', 'creator', 'creation_date', 'tags',
'priority', 'type', 'public_chat', 'community_public_chats', 'subscribed_users_count')
我知道有 3 个不同的 'fields' 会令人困惑,所以我澄清一下:
在我将参数 'fields' 传递给序列化程序的视图中,这是一个包含要从序列化程序中动态删除的字段名称的元组。然后在 init 中取出这个参数(因此它不会发送到超类)并将其分配给本地元组 'fields'。最后,在 'if fields is not None' 中,我从 self.fields(这些是序列化程序中定义的字段)中删除名称与本地元组中的字段匹配的字段。我希望我现在能解释得更好。
这是一个视频,展示了调试和在断点处停止时如何正常工作:http://youtu.be/RImEMebBGLY
解决方案:首先,在我看来,pydevd 或 Django Rest Framework 存在一个错误,这使得我的代码在调试模式下工作并在断点处停止(在视频中显示)。但我不是 Django 专家/Python 所以这可能是预期的行为。
如上面的代码所示,ChHiveLevel1Serializer 序列化程序具有嵌套的序列化程序,例如 ChPublicChatLevel1Serializer 就是其中之一。让我们看看这个序列化器的样子:
class ChCommunityPublicChatListLevel1Serializer(serializers.ModelSerializer):
"""Used by the following API methods: GET hive list,
"""
chat = serializers.SlugRelatedField(read_only=True, slug_field='chat_id', allow_null=True)
class Meta:
model = ChCommunityPublicChat
fields = ('chat')
如错误所述,字段被定义为字符串而不是元组。正确的形式是:
class ChCommunityPublicChatListLevel1Serializer(serializers.ModelSerializer):
"""Used by the following API methods: GET hive list,
"""
chat = serializers.SlugRelatedField(read_only=True, slug_field='chat_id', allow_null=True)
class Meta:
model = ChCommunityPublicChat
fields = ('chat', )
虽然我同意我在定义这个元组时犯了一个错误,但我仍然不明白为什么将调试器设置为 ON,它就可以正常工作。 (就像使用调试器并在断点处停止一样,它突然将 ('chat') 解释为元组而不是字符串)。