我正在尝试制作一个表单,允许用户上传文件(纯文本词典文件)。这是我的型号:
from django.conf import settings
from django.db import models
import uuid
class Dictionary(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
file = models.FileField(upload_to='uploads/%y/%m/%d')
timestamp = models.DateTimeField(auto_now_add=True)
这是我使用的表单。我只想公开文件参数供用户设置。我打算从请求用户设置所有者:
from django.forms import ModelForm
from dic.models import Dictionary
class DictionaryForm(ModelForm):
class Meta:
model = Dictionary
fields = ['file']
def __init__(self, owner, *args, **kwargs):
self.owner = owner
super().__init__(*args, **kwargs)
这是我的视图代码,它将帖子数据、文件和用户发送到表单:
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from dic.forms import DictionaryForm
@login_required
def upload(request):
if request.method == 'POST':
form = DictionaryForm(request.user, request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/dic/upload_success')
else:
form = DictionaryForm(request.user)
return render(request, 'dic/upload.html', {'form': form})
这是我失败的测试代码:
def test_can_upload_file(self):
user = User.objects.create(username='foo')
user.save()
self.client.force_login(user)
self.assertEqual(Dictionary.objects.count(), 0)
file = StringIO(initial_value='fake data here')
response = self.client.post('/dic/upload', {'file':file})
self.assertEqual(response.status_code, 200)
断言行从不运行,因为首先发生错误。生成的错误在视图代码中,位于form.save()
。生成的错误为:
django.db.utils.IntegrityError: NOT NULL constraint failed: dic_dictionary.owner_id
两个问题。如何正确设置表单中的所有者值以避免出现此错误?既然is_valid()
应该检查不会发生错误,为什么form.save()
行会发生错误?
is_valid()
仅验证您在表单中指定的字段(在本例中为-file
)。表单是有效的,但稍后会出现错误,因为保存时不满足其他模型字段约束。
将owner
传递给表单是不起作用的,因为表单没有处理该字段。相反,您需要在表单验证后,但在保存之前,将owner
添加到模型中:
if request.method == 'POST':
form = DictionaryForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=False)
obj.owner = request.user
obj.save()
return HttpResponseRedirect('/dic/upload_success')
(同时删除表单上的__init__
方法)。