Как я могу применить фильтр к вложенному ресурсу в Django Rest framework?
в моем приложении у меня есть следующие модели:
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 framework для создания ресурса, который возвращает сведения о зоне плюс вложенный ресурс, показывающий разрешения аутентифицированного пользователя для этой зоны. Вывод должен быть примерно таким:
{
"name": "test",
"current_user_zone_permission": {
"is_administrator": true,
"is_active": true
}
}
Я создал сериализаторы вот так:
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 к вложенному ресурсу?
кстати, я не хочу использовать HyperlinkedIdentityField для этого (чтобы минимизировать http-запросы).
решение
это решение я реализовал на основе ответов ниже. Я добавил следующий код в свой класс сериализатора:
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
большое спасибо за решение!
5 ответов:
я столкнулся с тем же сценарием. Лучшее решение, которое я нашел-это использовать
SerializerMethodFieldи этот метод запрашивает и возвращает нужные значения. Вы можете иметь доступ кrequest.userв этом методе черезself.context['request'].user.тем не менее, это кажется немного Хак. Я довольно новичок в DRF, поэтому, возможно, кто-то с большим опытом может вмешаться.
вы должны использовать фильтр вместо get, в противном случае при возврате нескольких записей вы получите исключение.
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.фильтр(**your_filters) до того, как метод называется. Затем вам нужно добавить свой подкласс ListSerializer как класс list_serializer_class на мете вложенного сериализатора.
- подкласс ListSerializer, перезапись to_representation и затем вызов super
- добавить подкласс ListSerializer в качестве meta list_serializer_class на вложенный сериализатор
Если вы используете QuerySet / filter в нескольких местах, вы можете используйте функцию getter на вашей модели, а затем даже отбросьте "исходный" кварг для сериализатора / поля. DRF автоматически вызывает функции / вызываемые объекты, если он находит их при использовании это get_attribute.
class Zone(models.Model): name = models.SlugField() def current_user_zone_permission(self): return ZonePermission.objects.get(zone=self, user=user)мне нравится этот метод, потому что он поддерживает согласованность вашего API под капотом с api по HTTP.
class ZoneSerializer(serializers.HyperlinkedModelSerializer): current_user_zone_permission = ZonePermissionSerializer() class Meta: model = Zone fields = ('name', 'current_user_zone_permission')надеюсь, это помогает некоторым людям!
Примечание: имена не нужно чтобы соответствовать, вы все еще можете использовать исходный кварг, если вам нужно/хотите.
Edit: я только что понял, что функция на модели не имеет доступа к пользователю или запросу. Поэтому, возможно, пользовательская модель field / ListSerializer будет более подходящей для этой задачи.
Я бы сделал это одним из двух способов.
1) либо сделать это через prefetch в вашем представлении:
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) или сделать это хотя .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
Comments