Python辞書の更新と外部キー深層クエリ、DRFでの一括操作実装

辞書のupdateメソッド

既存キーの値を上書きしたり、新規キー・バリューを追加する機能を持つ。引数には辞書やキー・バリューのイテラブルを受け取り、戻り値はなし。

d = {'apple': 1}
d.update({'banana': 2})
d.update(apple='red')
print(d)  # {'apple': 'red', 'banana': 2}

外部キー関連データの深層取得手法

  • ネストしたシリアライザを使用
  • depthオプションで階層指定
  • @propertyデコレータで動的フィールド生成

DRFにおける一括操作API設計

class BookAPIView(APIView):
    def delete(self, request, *args, **kwargs):
        target_ids = [kwargs.get('pk')] if kwargs.get('pk') else request.data
        updated_count = Book.objects.filter(
            is_deleted=False, 
            id__in=target_ids
        ).update(is_deleted=True)
        
        return Response({
            'status': 'success' if updated_count else 'failed',
            'affected': updated_count
        })

    def post(self, request, *args, **kwargs):
        is_bulk = isinstance(request.data, list)
        serializer = BookSerializer(
            data=request.data, 
            many=is_bulk
        )
        serializer.is_valid(raise_exception=True)
        saved_items = serializer.save()
        
        return Response(BookSerializer(
            saved_items, 
            many=is_bulk
        ).data)

    def put(self, request, *args, **kwargs):
        items = request.data if isinstance(request.data, list) else [request.data]
        item_ids = []
        for item in items:
            item_ids.append(item.pop('id'))
            
        existing_books = Book.objects.filter(id__in=item_ids)
        if len(existing_books) != len(item_ids):
            raise ValidationError("一部のIDが存在しません")

        serializer = BookSerializer(
            existing_books, 
            data=items, 
            many=True
        )
        serializer.is_valid(raise_exception=True)
        updated_books = serializer.save()
        
        return Response(BookSerializer(updated_books, many=True).data)

many=True時の内部処理フロー解析

class BaseSerializer:
    def __new__(cls, *args, **kwargs):
        if kwargs.pop('many', False):
            return cls.create_list_serializer(*args, **kwargs)
        return super().__new__(cls)

    @classmethod
    def create_list_serializer(cls, *args, **kwargs):
        child_instance = cls(*args, **kwargs)
        meta = getattr(cls, 'Meta', None)
        list_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_class(child=child_instance, *args, **kwargs)

class CustomListSerializer(serializers.ListSerializer):
    def update(self, instances, validated_data):
        return [
            self.child.update(instances[i], data) 
            for i, data in enumerate(validated_data)
        ]

class BookSerializer(serializers.ModelSerializer):
    title = serializers.CharField(max_length=64)  # unique制約を回避
    
    class Meta:
        list_serializer_class = CustomListSerializer

ハマりやすいユニーク制約の問題

ModelSerializerでunique=Trueなフィールドを扱う際、明示的にフィールド定義しないとモデルレベルの制約が適用され、更新時に誤って重複エラーが発生することがある。明示的なフィールド宣言でこの挙動を抑制できる。

タグ: Python Django-REST-Framework Serializer Dictionary ForeignKey

5月17日 04:08 投稿