python DRF是否不支持带有多部分/表单数据请求的嵌套序列化程序?

mwg9r5ms  于 2023-01-01  发布在  Python
关注(0)|答案(1)|浏览(115)

我想使用包含嵌套对象和文件的表单数据来发布请求,但我找不到解决问题的方法。有人知道这是否可行吗?或者我必须编写一些自定义的东西?
我正在尝试使用FormData发送一个文件,并且在该FormData对象中还有一个JSON对象。(我在JS中的客户端变量,我使用对其他API视图的API调用来获取所有信息,然后我将所有信息传递给JS.Variable中的客户端变量,我将该变量字符串化并附加到FormData对象中。)
我收到一个错误消息,说我的客户端是必需的,但是我忽略了它。
有人知道我是否做错了什么吗?或者DRF只是不知道如何解析具有嵌套对象的FormData对象。
我的JS代码:

$(document).ready(function () {
    
    $('#form_add').submit(function (event) {
        event.preventDefault();
        
        let input_atasament = document.getElementById('input_atasament');
        let client_pk   = $("#input_clienti").val();
        let nr_data     = "test";
        let tip         = $("#input_tip").val();
        let obiectul    = $("#input_obiectul").val();
        let termen      = $("#input_termen").val();
        const url       = `/api/contract-post/`;
        let client_url  = `/api/client-get/${client_pk}`;
        let client      = $.ajax({    
                            url: client_url,
                            async: false,
                            dataType: 'json' 
                        }).responseJSON;
       

        let client_string = JSON.stringify(client);
        
        let data = new FormData();
        data.append('nr_data', "test");
        data.append('tip', tip);
        data.append('obiectul', obiectul);
        data.append('termen', termen);
        data.append('atasament', input_atasament.files[0]);
        data.append('client', client_string);
        for(var p of data.entries())
        {
            console.log(p[1]);
        }

        var action = function(d) {
            console.log(d);
        };

        $.ajax({
            url: url,
            data: data,
            headers: {
                'X-CSRFToken': csrftoken
            },
            type: "POST",
            contentType: false,
            processData: false,
            success: action,
            error: action
        });

    });

});

我的API视图:

@api_view(['POST'],)
def contract_post(request):
    
    contract = Contract()
    
    if request.method == 'POST':
        serializer = ContractSerializer(contract, data=request.data)
        data = {}
        if serializer.is_valid():
            serializer.create(validated_data=request.data)
            handle_uploaded_file(request.FILES['atasament'])
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

def handle_uploaded_file(f):
    with open(f.name, 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

我的模特:

class Contract(models.Model):
    TIPURI = (
        ('P', 'Proiectare'),
        ('E', 'Executie'),
        ('D', 'Documentatie'),
    )

    tip             = models.CharField(max_length=300, choices=TIPURI)
    nr_data         = models.CharField(max_length=50)
    obiectul        = models.TextField()
    termen          = models.DateField(default=datetime.date.today)
    atasament       = models.FileField(upload_to='main/documents/', blank=True, null=True) 
    # For the moment it just need to set up stuff in settings, 
    #  after that I should make a file server with a NFS share or something
    client      = models.ForeignKey(Client, on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return self.nr_data
    
    class Meta:
        verbose_name_plural = "Contracte"

我的序列化程序:

class ContractSerializer(serializers.ModelSerializer):
    client      = ClientSerializer(read_only=False)
    termen      = serializers.DateField(format='%Y-%m-%d', default=datetime.date.today)
    atasament   = serializers.FileField(max_length=None, allow_empty_file=True)

    class Meta:
        model   = Contract
        fields  = (
            'pk',
            'tip',
            'nr_data',
            'obiectul',
            'termen',
            'atasament',
            'client',
        )
    
    
    def create(self, validated_data):
            client = validated_data.pop('client')
            client_instance, created = Client.objects.get_or_create(**client)
            contract_instance = Contract.objects.create(**validated_data, client=client_instance)
            return client_instance

我的形式:

<form id="form_add" enctype="multipart/form-data">
    {% csrf_token %}
    <div class="align-items-center">
        
            <div class="form-group">
            <label for="input_tip">Tip:</label>
            <select class="form-control" name="input_tip" id="input_tip">
                <option value="P">Proiectare</option>
                <option value="E">Execuție</option>
                <option value="D">Documentație</option>
            </select>
            </div>
            <div class="form-group">
            <label for="input_obiectul">Obiectul Contractului:</label>
            <textarea class="form-control" name="Obiectul Contractului" id="input_obiectul" rows="3"></textarea>
            </div>
            <div class="form-group">
            <label for="input_termen">Termen:</label>
            <input type="date" class="form-control" id="input_termen" placeholder="Termen">
            </div>
            <div class="form-group">
            <label for="input_atasament">Atașament:</label>
            <input type="file" class="form-control" id="input_atasament">
            </div>
            <div class="form-group">
            <label for="input_clienti">Client:</label>
            <select name="input_clienti" class="form-control" id="input_clienti"></select>
            <a class="btn btn-link"  style="font-size: normal; color: black; border: 1px solid grey; margin-top: 1em;" href="/clienti/add">Adaugă un client nou</a>
            </div>
            <div class="text-center alert alert-danger" hidden id="error_box">
            </div>
            <div class="text-center">
                <button class="btn btn-primary" type="submit">Adaugă</button>
            </div>
            <div class="text-center" style="top: 10px;">
                <a class="btn" href="/contracte">Înapoi</a>
            </div>
        
    </div>
</form>

我添加了我所有的代码,也许我做错了什么(很可能)。

6xfqseft

6xfqseft1#

class A(Serializer):
    pass

class B(Serializer):
    nested_a = A(many=True, read_only=True)
    nested_a_json = JsonField(write_only=True)

    def validate_nested_a_json(self, value):
        if not isinstance(value, list):
            ValidationError("nested_a_json expects a list")

        for item in value:
            serializer = A(data=item)
            serializer.is_valid(raise_exception=True)
        return value

相关问题