programing tip

Django REST 프레임 워크의 중첩 된 리소스에 필터를 적용하려면 어떻게해야합니까?

itbloger 2020. 12. 26. 09:35
반응형

Django REST 프레임 워크의 중첩 된 리소스에 필터를 적용하려면 어떻게해야합니까?


내 앱에는 다음 모델이 있습니다.

class Zone(models.Model):
    name = models.SlugField()

class ZonePermission(models.Model):
    zone = models.ForeignKey('Zone')
    user = models.ForeignKey(User)
    is_administrator = models.BooleanField()
    is_active = models.BooleanField()

Django REST 프레임 워크를 사용하여 영역 세부 정보와 해당 영역에 대한 인증 된 사용자의 권한을 보여주는 중첩 된 리소스를 반환하는 리소스를 만듭니다. 출력은 다음과 같아야합니다.

{
    "name": "test", 
    "current_user_zone_permission": {
        "is_administrator": true, 
        "is_active": true
    }
} 

다음과 같이 serializer를 만들었습니다.

class ZonePermissionSerializer(serializers.ModelSerializer):
    class Meta:
        model = ZonePermission
        fields = ('is_administrator', 'is_active')

class ZoneSerializer(serializers.HyperlinkedModelSerializer):
    current_user_zone_permission = ZonePermissionSerializer(source='zonepermission_set')

    class Meta:
        model = Zone
        fields = ('name', 'current_user_zone_permission')

이 문제는 특정 영역을 요청할 때 중첩 된 리소스가 해당 영역에 대한 권한이있는 모든 사용자 의 ZonePermission 레코드를 반환 한다는 것입니다. request.user중첩 된 리소스 에 필터를 적용하는 방법이 있습니까?

BTW 나는 HyperlinkedIdentityField이것을 사용하고 싶지 않습니다 (http 요청을 최소화하기 위해).

해결책

이것은 아래 답변을 기반으로 구현 한 솔루션입니다. serializer 클래스에 다음 코드를 추가했습니다.

current_user_zone_permission = serializers.SerializerMethodField('get_user_zone_permission')

def get_user_zone_permission(self, obj):
    user = self.context['request'].user
    zone_permission = ZonePermission.objects.get(zone=obj, user=user)
    serializer = ZonePermissionSerializer(zone_permission)
    return serializer.data

솔루션에 대단히 감사합니다!


나는 같은 시나리오에 직면했다. 내가 찾은 가장 좋은 해결책은 a를 사용하고 SerializerMethodField해당 메서드 쿼리 를 사용 하고 원하는 값을 반환하는 것입니다. request.user통해 해당 방법 으로 액세스 할 수 있습니다 self.context['request'].user.

그래도 이것은 약간의 해킹처럼 보입니다. 나는 DRF를 처음 접했기 때문에 더 많은 경험을 가진 사람이 참여할 수 있습니다.


get 대신 filter를 사용해야합니다. 그렇지 않으면 여러 레코드가 반환되면 Exception이 발생합니다.

current_user_zone_permission = serializers.SerializerMethodField('get_user_zone_permission')

def get_user_zone_permission(self, obj):
    user = self.context['request'].user
    zone_permission = ZonePermission.objects.filter(zone=obj, user=user)
    serializer = ZonePermissionSerializer(zone_permission,many=True)
    return serializer.data

이제 여기에서 설명한 방법을 사용하여 ListSerializer를 하위 클래스로 만들 수 있습니다. https://stackoverflow.com/a/28354281/3246023

ListSerializer를 하위 클래스로 만들고 to_representation 메서드를 덮어 쓸 수 있습니다.

기본적으로 to_representation 메소드는 중첩 된 쿼리 세트에서 data.all ()을 호출합니다. 따라서 메서드가 호출되기 전에 효과적으로 data = data.filter (** your_filters)를 만들어야합니다. 그런 다음 하위 클래스 화 된 ListSerializer를 중첩 된 serializer의 메타에 list_serializer_class로 추가해야합니다.

  1. ListSerializer 하위 클래스, _representation 덮어 쓰기 및 super 호출
  2. 중첩 된 Serializer의 메타 list_serializer_class로 서브 클래 싱 된 ListSerializer를 추가합니다.

여러 위치에서 QuerySet / 필터를 사용하는 경우 모델 에서 getter 함수를 사용한 다음 Serializer / Field에 대한 'source'kwarg를 삭제할 수도 있습니다. DRF는 get_attribute 함수를 사용할 때 찾으면 자동으로 함수 / 호출 가능을 호출 합니다.

class Zone(models.Model):
    name = models.SlugField()

    def current_user_zone_permission(self):
        return ZonePermission.objects.get(zone=self, user=user)

I like this method because it keeps your API consistent under the hood with the api over HTTP.

class ZoneSerializer(serializers.HyperlinkedModelSerializer):
    current_user_zone_permission = ZonePermissionSerializer()

    class Meta:
        model = Zone
        fields = ('name', 'current_user_zone_permission')

Hopefully this helps some people!

Note: The names don't need to match, you can still use the source kwarg if you need/want to.

Edit: I just realised that the function on the model doesn't have access to the user or the request. So perhaps a custom model field / ListSerializer would be more suited to this task.


I would do it in one of two ways.

1) Either do it through prefetch in your view:

    serializer = ZoneSerializer(Zone.objects.prefetch_related(
        Prefetch('zone_permission_set', 
            queryset=ZonePermission.objects.filter(user=request.user), 
            to_attr='current_user_zone_permission'))
        .get(id=pk))

2) Or do it though the .to_representation:

class ZoneSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Zone
        fields = ('name',)

    def to_representation(self, obj):
        data = super(ZoneSerializer, self).to_representation(obj)
        data['current_user_zone_permission'] = ZonePermissionSerializer(ZonePermission.objects.filter(zone=obj, user=self.context['request'].user)).data
        return data

ReferenceURL : https://stackoverflow.com/questions/16821684/how-can-i-apply-a-filter-to-a-nested-resource-in-django-rest-framework

반응형