通过Vuejs前端向Django模型Formset的POST请求

时间:2020-06-04 17:59:22

标签: django django-forms

我正在使用Django构建项目的后端,并使用Vuejs构建前端。

我已经在Django中定义了模型,并使用Django表单将数据保存到模型中。对于单个表单,我只需在POST请求的主体中传递一个JSON对象。这工作得很好。

但是现在我需要同时保存多个记录。我发现Django模型表单集可以做到这一点。但是,我不知道如何格式化POST请求。 Django文档假定我将使用Django模板(这很烦人),因此对请求本身的格式一无所知。

该应用程序是API文档工具,我需要允许用户定义API的参数。到目前为止,我有以下内容:

from django.forms import modelformset_factory
from docs.models import ApiParams

ParamsFormSet = modelformset_factory(ApiParams, exclude=('param_id',))

def create_params(request):
    body_unicode = request.body.decode('utf-8')
    body = json.loads(body_unicode)
    formset = ParamsFormSet(body)
    instaces = formset.save()
    return HttpResponse(instances)

我使用以下数据调用了此API:

[
    {
        "api": 2,
        "param_name": "param1",
        "param_type": "string",
        "param_required": true,
        "param_sample": "param"
    },
    {
        "api": 2,
        "param_name": "param2",
        "param_type": "numeric",
        "param_required": false,
        "param_sample": "3"
    }
]

但这会导致以下错误:

Internal Server Error: /create-params/
Traceback (most recent call last):
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "D:\Documents\django\api-docs\docs\views.py", line 47, in create_api_params
    if formset.is_valid():
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\formsets.py", line 308, in is_valid
    self.errors
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\formsets.py", line 288, in errors
    self.full_clean()
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\formsets.py", line 329, in full_clean
    for i in range(0, self.total_form_count()):
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\formsets.py", line 112, in total_form_count
    return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\utils\functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\formsets.py", line 91, in management_form
    if not form.is_valid():
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\forms.py", line 180, in is_valid
    return self.is_bound and not self.errors
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\forms.py", line 175, in errors
    self.full_clean()
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\forms.py", line 376, in full_clean
    self._clean_fields()
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\forms.py", line 388, in _clean_fields
    value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
  File "D:\Documents\django\api-docs\venv\lib\site-packages\django\forms\widgets.py", line 258, in value_from_datadict
    return data.get(name)
AttributeError: 'list' object has no attribute 'get'

P.S。我的问题与Vuejs没有任何关系,只是为了避免人们建议Django模板解决方案而提到Vuejs。我向Postman提出了上述POST请求,甚至还没有将API与我的前端集成。

1 个答案:

答案 0 :(得分:1)

因此,让我们看看是否在DRF中执行此操作。我假设您的ApiParam模型是这样的:

_param_types = ["string", "integer", "array", "object"]
PARAM_TYPE_CHOICES = [(t, t) for t in _param_types]

class ApiParam(models.Model):
    api = models.ForeignKey(Api, on_delete=models.CASCADE, related_name="parameters")
    param_name = models.CharField(max_length=100)
    param_type = models.CharField(max_length=10, choices=PARAM_TYPE_CHOICES)
    param_required = models.BooleanField(default=False)
    param_sample = models.TextField()

序列化为:

class ApiParamSerializer(serializers.ModelSerializer):
    class Meta:
        model = ApiParam
        fields = [  # Explicit > Implicit
            "param_name",
            "param_type",
            "param_required",
            "param_sample"
        ]

从标题中的表单集来看,您对有效负载的意图似乎是使用其他参数更新Api模型,该参数是反向外键甚至是ManyToMany。

在这种情况下,最好将有效载荷构造为:

{
    "api": 2,
    "parameters": [
        {
            "param_name": "param1",
            "param_type": "string",
            "param_required": true,
            "param_sample": "param"
        },
        {
            "param_name": "param2",
            "param_type": "numeric",
            "param_required": false,
            "param_sample": "3"
        }
    ]
}

您的ApiSerializer将是:

class ApiSerializer(serializers.ModelSerializer):
    api = serializers.IntegerField(source='api_id')
    parameters = ApiParamSerializer(many=True)
    class Meta:
        model = Api
        fields = ["api", "parameters"]

您的看法:

class ApiUpdateView(generics.UpdateAPIView):
    serializer_class = ApiSerializer

我们应该做。如您所见,这都是声明性的。繁重的工作由Django / DRF完成。这需要一点时间来适应,但是一旦四分之一下降,它就非常简单。