django ModelForm外鍵問題

背景

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了進不去鈎子函數。如果以後找到更好的辦法再修改該方案

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值