背景
django在使用ModelForm時如果存在外鍵字段,默認是ChoiceField讓你選擇外鍵關聯表有的值,但是如果關聯表的數據很多的話選擇就很難找到選項。所以想能不能換成輸入框TextInput。
舉個例子
models.py
這裏建了兩個表,把用戶表的name作爲外鍵加入詳情表
class User(models.Model):
""" 用戶表 """
name= models.CharField(verbose_name="用戶", max_length=32)
class Detail(models.Model):
""" 详情表 """
name= models.ForeignKey(to="User", to_field="id", on_delete=models.CASCADE)
asd= models.IntegerField(verbose_name="asd")
detail_add.html
用模板渲染引擎把前端展示出來
<div class="panel-body">
<form method="post" novalidate>
{% csrf_token %}
{% for filed in form %}
<div class="form-group">
<label>{{ filed.label }}</label>
{{ filed }}
<span style="color: red">{{ filed.errors.0 }}</span>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
用了bootstrap樣式才長這樣,本來是最原始的下拉框
view.py
class DetailModelForm(forms.ModelForm):
""" 定義一個ModelForm類 """
class Meta:
model = Detail
fields = '__all__'
class AddView(View):
""" 添加用戶詳情 """
def get(self, request):
form = DetailModelForm()
return render(request, 'detail_add.html', {'form': form})
def post(self, request):
# 用户POST提交数据,数据校验
data = request.POST
form = DetailModelForm(data=data)
if form.is_valid():
# 如歌数据合法,保存到数据库
# print(form.cleaned_data)
form.save()
return redirect('/index/')
else:
# 校验失败
# print(form.errors)
return render(request, 'detail_add.html', {'form': form})
問題描述及解決
修改ModelForm類把外鍵字段改成TextInput
class DetailModelForm(forms.ModelForm):
class Meta:
model = Detail
fields = '__all__'
widgets = {
'name': forms.TextInput,
}
報錯
雖然成功變成一個輸入框,但是輸入提交報以下錯誤
选择一个有效的选项: 该选择不在可用的选项中。
解決思路
雖然不知道是爲什麽也搜不到解答,感覺可能是沒能索引到外鍵的表
然後我就打個斷點用debug看一下到底每一步怎麽走的
爲此我又創建一個country字段讓他作爲外鍵,依舊保持ChoiceField,看看不修改成TextInput和修改成TextInput有什麽區別
圈起來的兩個字段就是外鍵字段,可以發現在author字段是名字 但是在country字段是數字。這個數字是什麽?爲什麽是數字?查了一下數據庫發現是country的值在數據庫的id
那就明白了,ChoiceField字段接收到的是id但是TextInput收到的就是字符串,那我們轉換成id傳給ModelForm不就可以了
解決方案
class AddView(View):
""" 添加用戶詳情 """
def get(self, request):
form = DetailModelForm()
return render(request, 'detail_add.html', {'form': form})
def post(self, request):
# 用户POST提交数据,数据校验
# 修改版本
author = request.POST.get('author') # 得到表單中author
author = Author.objects.get(author=author).pk # 得到該人的id,pk是主鍵的意思這裏用 .id 也是一樣
data = request.POST
data['author'] = str(author) # 把裏面的名字轉換為id
form = DetailModelForm(data=data)
if form.is_valid():
# 如歌数据合法,保存到数据库
# print(form.cleaned_data)
form.save()
return redirect('/index/')
else:
# 校验失败
# print(form.errors)
return render(request, 'detail_add.html', {'form': form})
報錯
理論上感覺沒錯了,但是運行還是報以下錯
AttributeError: This QueryDict instance is immutable
問題分析
因为默认的 QueryDict 是不可修改的。解决办法就是复制一份副本,对副本进行修改
解決方案
request.POST = request.POST.copy()
view.py修改版本:
class AddView(View):
""" 添加用戶詳情 """
def get(self, request):
form = DetailModelForm()
return render(request, 'detail_add.html', {'form': form})
def post(self, request):
# 用户POST提交数据,数据校验
# 修改版本
author = request.POST.get('author') # 得到表單中author
author = Author.objects.get(author=author).pk # 得到該人的id,pk是主鍵的意思這裏用 .id 也是一樣
request.POST = request.POST.copy()
data = request.POST
data['author'] = str(author) # 把裏面的名字轉換為id
form = DetailModelForm(data=data)
if form.is_valid():
# 如歌数据合法,保存到数据库
# print(form.cleaned_data)
form.save()
return redirect('/index/')
else:
# 校验失败
# print(form.errors)
return render(request, 'detail_add.html', {'form': form})
報錯
如果輸入一個數據庫不存在的名字,那麽Author.objects.get(author=author).pk會報錯
raise self.model.DoesNotExist(
解決方案
用try…except
try:
author = Author.objects.get(author=author).pk
data['author'] = str(author)
form = DetailModelForm(data=data)
except Author.DoesNotExist:
form = DetailModelForm(data=data)
# form.add_error('author', '作者不存在')
form.errors.get('author')[0] = '作者不存在'
view.py最終版本:
class AddView(View):
""" 添加用戶詳情 """
def get(self, request):
form = DetailModelForm()
return render(request, 'detail_add.html', {'form': form})
def post(self, request):
# 用户POST提交数据,数据校验
# 最終版本
request.POST = request.POST.copy()
data = request.POST
author = request.POST.get('author')
try:
author = Author.objects.get(author=author).pk
data['author'] = str(author)
form = DetailModelForm(data=data)
except Author.DoesNotExist:
form = DetailModelForm(data=data)
# form.add_error('author', '作者不存在')
form.errors.get('author')[0] = '作者不存在'
if form.is_valid():
# 如歌数据合法,保存到数据库
# print(form.cleaned_data)
form.save()
return redirect('/index/')
else:
# 校验失败
# print(form.errors)
return render(request, 'detail_add.html', {'form': form})
總結
這樣雖然成功解決,但是我感覺ModelForm應該本身就有解決這個問題的辦法,本來我想用鈎子函數去解決,但是form.is_valid()就是False了進不去鈎子函數。如果以後找到更好的辦法再修改該方案