ограничить поля django, запрашиваемые в sql вызове базы данных набором запросов - PullRequest
1 голос
/ 23 мая 2019

У меня есть таблица с большим двоичным объектом, и я хотел бы исключить ее из вызова sql в базу данных, если это не требуется специально.Из коробки django включает все в набор запросов.До сих пор я нашел единственный способ ограничить поле - добавить функцию в представление get_queryset ()

def filter_queryset_fields(request, query_model):
    fields = request.query_params.get('fields')
    if fields:
        fields = fields.split(',')
        # Drop any fields that are not specified in the `fields` argument.
        allowed = set(fields)
        existing = set([f.name for f in query_model._meta.get_fields()])
        values = []
        for field_name in existing & allowed:
            values.append(field_name)
        queryset = query_model.objects.values(*values)
    else:
        queryset = query_model.objects.all()
    return queryset


class TestViewSet(DynamicFieldsMixin, viewsets.ReadOnlyModelViewSet):
    queryset = models.TestData.objects.all()
    serializer_class = serializers.TestSerializer

    filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
    filter_fields = ('id', 'frame_id', 'data_type')

    def get_queryset(self):
        return filter_queryset_fields(self.request, models.TestData)

и смешать с сериализатором, чтобы ограничить поля, которые он проверяет

class DynamicFieldsMixin(object):
    def __init__(self, *args, **kwargs):
        super(DynamicFieldsMixin, self).__init__(*args, **kwargs)
        if "request" in self.context and self.context['request'] is not None:
            fields = self.context['request'].query_params.get('fields')
            if fields:
                fields = fields.split(',')
                # Drop any fields that are not specified in the `fields` argument.
                allowed = set(fields)
                existing = set(self.fields.keys())
                for field_name in existing - allowed:
                    self.fields.pop(field_name)


class TestSerializer(DynamicFieldsMixin,     rest_serializers.ModelSerializer):
    class Meta:
        model = models.TestData
        fields = '__all__'

это похоже на большой код для того, что он делает.Есть ли более простой способ?

Ответы [ 3 ]

0 голосов
/ 23 мая 2019

Использовать значения () :

MyModel.objects.values('column1', 'column2')
0 голосов
/ 23 мая 2019

Я бы сделал что-то подобное для одиночных, не вложенных объектов.Для вложенных свойств вам понадобится больше логики.

class DynamicFieldListSerializer(serializers.ListSerializer):

    def to_representation(self, data):
        """
        Code is a copy of the original, with a modification between the
        iterable and the for-loop.
        """
        # Dealing with nested relationships, data can be a Manager,
        # so, first get a queryset from the Manager if needed
        iterable = data.all() if isinstance(data, models.Manager) else data

        fields = list(self.child.get_fields().keys())
        iterable = iterable.only(*fields)

        return [
            self.child.to_representation(item) for item in iterable
        ]


class DynamicSerializerFieldsMixin:

    def get_fields(self):
        fields = super().get_fields()
        raw_fields = set(self.context['request'].GET.get('fields', '').split(','))
        # If querysparams ?fields= doesn't evaluate to anything, default to original
        validated_fields = set(raw_fields) & set(fields.keys()) or set(fields.keys())
        return {key: value for key, value in fields.items() if key in validated_fields}

    @classmethod
    def many_init(cls, *args, **kwargs):
        meta = getattr(cls, 'Meta', None)
        if not hasattr(meta, 'list_serializer_class'):
            meta.list_serializer_class = DynamicFieldListSerializer
        return super().many_init(*args, **kwargs)

Примеры см .:

https://gist.github.com/kingbuzzman/d7859d9734b590e52fad787d19c34b52#file-django_field_limit-py-L207

0 голосов
/ 23 мая 2019

Джанго уже получил это прямо из коробки.Просто используйте defer или only

Defer позволит вам исключить набор полей из набора запросов:

MyModel.objects.defer('field_i_want_to_exclude')

Хотя позволяет толькочтобы сказать, какие поля вы хотите в наборе запросов:

MyModel.objects.only('field_i_want1', 'field_i_want2')
...