Django RESTAPI如何使计算API PUT或POST?

scyqe7ek  于 2023-06-25  发布在  Go
关注(0)|答案(1)|浏览(127)

你好,我正在尝试使用django制作计算REST API。
我的目标是将preprocessed_data(result)保存到db表字段中
这个过程是从数据库读取原始数据路径(txt文件),然后使用pandas & numpy进行计算,然后将结果txt文件保存到数据库预处理数据字段中。
这是我的models.py

class PreprocessData(models.Model):

    raw = models.FileField(
        upload_to=_user_directory_path,
        storage=OverwriteStorage(),

    preprocessed_data = models.CharField(
        max_length=200,
        null=True,
        blank=True,
    )
)

和views.py

class PreprocessCalculate(APIView):
    def _get_object(self, pk):
        try:
            data = PreprocessData.objects.get(id=pk)
            return data
        except PreprocessData.DoesNotExist:
            raise NotFound

#get or put or post which is the best design for api?

# PUT API
    def put(self, request, pk):
        data = self._get_object(pk)
        serializer = PreprocessCalculateSerializer(data, request.data, partial=True)
        if serializer.is_valid():
            updated_serializer = serializer.save()
            return Response(PreprocessDataSerializer(updated_serializer).data)
        else:
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

和serializers.py

class PreprocessResultField(serializers.CharField):
    def to_representation(self, value) -> str:
        ret = {"result": value.test_func()}
        return ret

    def to_internal_value(self, data):
        return data["result"]

class PreprocessCalculateSerializer(serializers.ModelSerializer):
    preprocessed_data = PreprocessResultField()

    class Meta:
        model = PreprocessData
        fields = ("uuid", "preprocessed_data")

我的问题是
1.使用上面的代码。在数据库中,“preprocessed_field”仍然为空...自定义字段序列化程序中的问题是什么?
1.我选择“PUT”方法来计算原始文件,但我认为如果我使用“PUT”,它有一个问题,改变我的“uuid”的错误。我觉得不好。那么我应该使用GET还是POST?做restAPI计算?如果“PUT”是对的,如何保持我的数据库幂等?
请帮帮我……

u59ebvdq

u59ebvdq1#

实际上,您可以使用任何您想要的方法(尽管有一些principles与之相关)。另一件重要的事情是,你需要知道你要处理什么样的文件。下面是一个简单的例子,它只接受.csv文件上传,读取其数据并根据request.method的类型执行不同的过程:
models.py

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return "user_{0}/{1}".format(instance.user.id, filename)

class PreprocessData(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    raw = models.FileField(upload_to=user_directory_path)
    result = models.CharField(max_length=255)

views.py (extra context on serializer)

class PreprocessCalculate(views.APIView):
    def post(self, request, format=None):
        serializer = PreprocessDataSerializer(
            data=request.data, context={"method": request.method}
        )
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

    def put(self, request, format=None):
        serializer = PreprocessDataSerializer(
            data=request.data, context={"method": request.method}
        )
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

processors.py

def post_pre_process(file):
    data = pd.read_csv(file, header=None)
    calculation = data.to_numpy().sum()
    return calculation

def put_pre_process(file):
    data = pd.read_csv(file, header=None)
    calculation = data.to_numpy().prod()
    return calculation

serializers.py

class PreprocessDataSerializer(serializers.ModelSerializer):

    class Meta:
        model = PreprocessData
        fields = ['user', 'raw', 'result']
        extra_kwargs = {
            'result': {'read_only': True}
        }
    
    def validate(self, attrs):
        """Only .csv files are accepted"""
        filename, ext = attrs["raw"].name.split(".")
        if not ext == "csv":
            raise serializers.ValidationError({"msg": "File must be .csv"})

        return super().validate(attrs)
    
    def create(self, validated_data):
        method = self.context.get('method')
        if method == 'POST':
            validated_data['result'] = post_pre_process(validated_data['raw'])
        if method == 'PUT':
            validated_data['result'] = put_pre_process(validated_data['raw'])
        return super().create(validated_data)

关于其他问题:
使用上面的代码。在数据库中,“preprocessed_field”仍然为空...自定义字段序列化程序中的问题是什么?
在你的代码中,你返回了一个不是string的对象,还有一个value.test_func(),它在任何地方都看不到,如果它返回一个字符串,那么你的.to_representation应该返回ret['result']
我选择“PUT”方法来计算原始文件,但我认为如果我使用“PUT”,它有一个问题,改变我的“uuid”的错误。我觉得不好。那么我应该使用GET还是POST?做restAPI计算?如果“PUT”是对的,如何保持我的数据库幂等?
正如我所说的,POSTPUT是好的。同样,如果editable=Falseuuid也不会改变。最后,对于两种请求,结果总是相同的。例如,假设一个.csv文件包含。

1,2,3,4,5

那么POST总是返回

{
    "user": 1,
    "raw": "/media/user_1/myfile_ciacf5j.csv",
    "result": "15"
}

同样的方式PUT

{
    "user": 1,
    "raw": "/media/user_1/myfile_bljuHte.csv",
    "result": "120"
}

相关问题