如何使用表单在 Django OneToOne 模型的 FileField 中上传文件?

How do I upload a file in Django OneToOne Model's FileField using form?

我有一个模型 Detail,它与默认 User 模型有 OneToOne 关系。我的 Detail 模型中有一个字段 FileField,我想在其中使用来自 frontend/templates.

的表单上传文件

我一直在努力解决它,但我没有完成它。我需要帮助。

我的models.py是:

from django.db import models
from django.contrib.auth.models import User

class Detail(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    file = models.FileField(verbose_name="CSV File", upload_to='csv_files/')
    file_desc = models.TextField("CSV File Description")

    def __str__(self):
        return ("{} ({} {})".format(self.user.email, self.user.first_name, self.user.last_name))

我的forms.py是:

from django.forms import ModelForm
from .models import Detail

class DetailForm(ModelForm):
    class Meta:
        model = Detail
        fields = ['file', 'file_desc']

我的views.py是:

from django.views import View

class UserAPI(View):
    template_name = 'accounts/user.html'

    def get(self, request):
        form = DetailForm(instance=request.user)

        context = {'form': form}
        return render(request, self.template_name, context)

    def post(self, request):
        form = DetailForm(request.POST, request.FILES, instance=request.user)
        if form.is_valid():
            form.save()
            return redirect('user')

        context = {'form': form}
        return render(request, self.template_name, context)

我的user.html (template)是:

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit</button>
</form>

每次我去 localhost:8000/user 填写表格并点击提交按钮,它都会在前端给我以下错误: No File Chosen 并且以下语句出现在文件选择按钮上方: This field is required.

非常感谢您的帮助。谢谢

更新:

urls.py

urlpatterns = [
    path('register', RegisterAPIHTML.as_view(), name='register'),
    path('login', LoginAPIHTML.as_view(), name='login'),
    path('user', UserAPI.as_view(), name='user'),
    path('logout', LogoutAPI.as_view(), name='logout'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

settings.py

STATIC_ROOT  =   os.path.join(BASE_DIR, 'staticfiles')

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

MEDIA_ROOT = os.path.join(BASE_DIR, 'data/')
MEDIA_URL = '/media/'

我认为您的表格没有保存任何内容,因为带有 form.is_valid() returns False.

的行

您不会在 form.errors 中看到任何内容,因为该属性仅显示字段错误,而不显示 non-field 错误。本例中的 non-field 错误是缺少必填字段 user。这是因为您没有在模型表单中指定它(也是为什么它被放置为 non-field 错误)。

此外,您没有在 HTML 中呈现 non-field 错误,这就是为什么在 post 提交后您在页面中看不到它们的原因。我建议使用 crispy-forms 包(这里是 link)。它处理开箱即用的表单渲染之类的事情。

此外,指定参数 ìnstance=request.user 是不正确的,因为如果您有 DetailForm 实例应该是 Detail 模型的模型实例,而不是 User 模型。请记住,仅当您想使用此表单进行更新时才需要 instance kwarg。

我建议采用类似于以下的实现方式:

forms.py

class DetailForm(ModelForm):
    class Meta:
        model = Detail
        fields = ['user', 'file', 'file_desc']

        widgets = {
            # I'm guessing you don't want to see the User field
            'user': HiddenInput()
        }

views.py

的相关部分
def get(self, request):
    form = DetailForm()

    context = {'form': form}
    return render(request, self.template_name, context)

def post(self, request):
    form = DetailForm(request.POST, request.FILES, initial={'user': request.user.id})
    if form.is_valid():
        form.save()
        return redirect('user')


    context = {'form': form}
    return render(request, self.template_name, context)

为了使这个问题成为一个完整的答案,我将包含来自 @raphael 的评论,它修复了您的代码的第一个问题:

Make sure your form tag is as follows: <form method="post" enctype="multipart/form-data">

要遵循的一些资源: