如何使用表单在 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">
要遵循的一些资源:
我有一个模型 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">
要遵循的一些资源: