DRF是否不支持具有多部分/formdata请求的嵌套序列化程序?

2024-09-29 06:31:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我想使用具有嵌套对象和文件的表单数据发布请求,但找不到问题的解决方案。有没有人知道这是否可能,或者我必须写一些定制的东西

我正在尝试使用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>

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


Tags: divformclientidforinputdatamodels