Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python / Django Rest Framework weird error happening only when using debugger

This question has been SOLVED by myself after better looking at it. Please read my answer below.


I am getting a "The fields option must be a list or tuple. Got str." when running my Django app. Running exactly the same code with the debugger, and if I have a breakpoint in the line of the error, then it won't fail and what should be a tuple seems to be a tuple.

The problem seems to be located in the following code inside a 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)

In the views.py I just do: ...

    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)
    ...

And this is the traceback:

    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

If I remove the print("fields to be included: ", self.fields) then I got the same error but it will point to the line serializer = serializers.ChHiveLevel1Serializer(hives, fields=fields, many=True) in the views.py

Ok, I am still a noob in Python and I could be doing something very wrong, but what I can't understand is why if I insert a breakpoint in the print I've just mentioned, and I do the same api request with the debug on, then the code just works: I get my response just as I wanted, and it doesn't give any error (if I remove the breakpoint it will again give the error even if launching with the debugger).

Do you guys have any idea of what could be wrong? Thanks a lot in advance. Ask me for any extra info if you need it!

EDIT: Further explanations:

The whole serializer is this:

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')

I know its confusing to have 3 different 'fields' so I clarify this:

in the views I am passing a param 'fields' to the serializer, this is a tuple that contain names of fields to be dynamically removed from the serializer. Then inside the init I take pop out this param (so it is not sent to the superclass) and assign it the local tuple 'fields'. Finally, inside the 'if fields is not None' I am removing from self.fields (these are the fields defined in the serializer) the fields with name matching those inside the local tuple. I hope I could explain it better now.

Here is a video showing how when debugging and stopping at the breakpoint it just works: http://youtu.be/RImEMebBGLY

like image 594
diegopau Avatar asked May 05 '15 15:05

diegopau


1 Answers

SOLUTION: First of all it seems to me that there is a bug with either pydevd or Django Rest Framework, that made my code work when in debugging mode and stoping in breakpoints (showed in the video). But I am not expert in Django / Python so it could be the expected behaviour.

As seen in the code above, the ChHiveLevel1Serializer serializer has nested serializers, being one of them, for example, ChPublicChatLevel1Serializer. Lets see how this serializer looks like:

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')

As the error said, fields is defined as a string instead of a tuple. The correct form would be:

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', )

While I agree that I made a mistake in how i defined this tuple, I still can't understand why with the debugger set to ON, it would just work. (like if using the debugger and stopping in breakpoints it suddenly interprets ('chat') as a tuple instead of a string).

like image 82
diegopau Avatar answered Oct 07 '22 06:10

diegopau