Skip to content

Changes to the get_attribute method under fields.py after version 3.6.x #7196

@csrftoken

Description

@csrftoken

When retrieving field information from source, if the object retrieved by a chain operation is None, it should be returned directly.

This operation was removed after version 3.6.x, if so I may need to make additional judgment, I think this action is friendly

What do you think?

Expected behavior

def get_attribute(instance, attrs):
    for attr in attrs:

        # Break out early if we get `None` at any point in a nested lookup.
        if instance is None:
            return None

        try:
            if isinstance(instance, Mapping):
                instance = instance[attr]
            else:
                instance = getattr(instance, attr)
        except ObjectDoesNotExist:
            return None
        if is_simple_callable(instance):
            try:
                instance = instance()
            except (AttributeError, KeyError) as exc:
                # If we raised an Attribute or KeyError here it'd get treated
                # as an omitted field in `Field.get_attribute()`. Instead we
                # raise a ValueError to ensure the exception is not masked.
                raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))

    return instance

Actual behavior

def get_attribute(instance, attrs):
    for attr in attrs:
        try:
            if isinstance(instance, Mapping):
                instance = instance[attr]
            else:
                instance = getattr(instance, attr)
        except ObjectDoesNotExist:
            return None
        if is_simple_callable(instance):
            try:
                instance = instance()
            except (AttributeError, KeyError) as exc:
                # If we raised an Attribute or KeyError here it'd get treated
                # as an omitted field in `Field.get_attribute()`. Instead we
                # raise a ValueError to ensure the exception is not masked.
                raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))

    return instance

Example

drf version:3.10.x

# models.py

from django.db import models

class B(models.Model):
    name = models.CharField(max_length=32)

class A(models.Model):
    field1 = models.ForeignKey("B", on_delete=models.CASCADE, null=True)

# serializer.py

class ASerializer(serializers.ModelSerializer):
    name = serializers.CharField(source="field1.name")
    class Meta:
        fields = ("name", )
>>> from . import models
>>> from . import serializer
>>> ins = models.A.objects.create()
>>> ser = serializer.A(ins)
>>> ser.data
...
AttributeError: Got AttributeError when attempting to get a value for field `name` on serializer `ASerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `B` instance.
Original exception text was: 'NoneType' object has no attribute 'name'.

I think the expected behavior is friendly and I will submit a PR if you approve.

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy