如何将QuerySet转换为Django表单choices字段可用的数据集并动态加载其他模型数据作为选项
看起来你现在的核心需求是:从Category_pk_for_form_category或Category_pk_for_form_maim模型中获取分类数据,把它转换成Django表单choices字段能识别的格式,动态赋值给Form_GPR的name_category字段作为单选选项对吧?我先帮你梳理下当前代码的问题,再给出正确的实现方式。
一、先纠正你当前代码里的关键错误
你在视图里处理choises_1的逻辑有明显问题:
choises_1 = Category_pk_for_form_maim.objects.all() choises_1 = choises_1.values_list("categoty_name", flat=True) choises_1 = choises_1.values() # 这一步完全错误!
当你用values_list("categoty_name", flat=True)时,返回的是包含所有categoty_name字符串的可迭代对象,但Django表单的choices要求的是二元组的可迭代对象(格式为[(存储值, 显示文本), ...]),而且你后面调用的.values()是画蛇添足,会直接抛出属性错误。
二、正确的QuerySet转Choices的方法
Django表单的choices字段必须接收二元组迭代器:第一个元素是要存入数据库的实际值,第二个是展示给用户的文本。针对你的场景,因为CreateNewGPR的name_category是CharField,存储的就是分类名称本身,所以我们可以直接把分类名称同时作为值和显示文本。
方式1:在视图中动态赋值(适配你当前的代码结构)
修正视图里的choises_1处理逻辑,生成符合要求的二元组列表:
# 方法1:用列表推导式生成二元组 choises_1 = [(cat.categoty_name, cat.categoty_name) for cat in Category_pk_for_form_maim.objects.all()] # 方法2:用values_list直接生成二元组(更高效) choises_1 = Category_pk_for_form_maim.objects.values_list("categoty_name", "categoty_name") # 赋值给表单字段 form_2.fields["name_category"].choices = choises_1
另外,你当前通过Category_pk_for_form_maim中转数据的逻辑有点冗余——其实可以直接从Category_pk_for_form_category过滤生成choices,跳过中间模型的删除/保存操作,减少数据库开销:
# 直接根据当前pk过滤分类数据,生成choices category_choices = Category_pk_for_form_category.objects.filter(name_object=pk).values_list("category_name", "category_name").distinct() form_2.fields["name_category"].choices = category_choices
方式2:在表单类中重写__init__方法(Django最佳实践)
如果这个表单需要在多个视图中使用,把动态加载choices的逻辑封装到表单类里会更优雅:
# forms.py class Form_GPR(forms.ModelForm): class Meta: model = CreateNewGPR fields = ("name_category", "name_working", "type_izm", "value_work", "lyudi_norma", "technik_norma", "date_begin", "date_end") widgets = { "name_category": forms.RadioSelect(attrs={"type":"radio"}), "date_begin": forms.DateInput(attrs={"class":"form-control", "type":"date"}), "date_end": forms.DateInput(attrs={"class":"form-control", "type":"date"}), } def __init__(self, *args, object_pk=None, **kwargs): super().__init__(*args, **kwargs) if object_pk: # 根据传入的object_pk过滤分类数据 category_choices = Category_pk_for_form_category.objects.filter(name_object=object_pk).values_list("category_name", "category_name").distinct() self.fields["name_category"].choices = category_choices else: # 没有传入pk时显示默认空选项 self.fields["name_category"].choices = [("", "请先选择对象")]
然后在视图里初始化表单时,传入object_pk参数即可:
# views.py form_2 = Form_GPR(object_pk=pk)
这种方式把表单逻辑封装在表单类中,更符合Django的MVC设计思想。
三、额外的模型结构优化建议
你当前的模型存在冗余,可以进一步优化来保证数据一致性:比如把CreateNewGPR的name_category从CharField改成ForeignKey,关联到专门的分类模型,这样能避免重复存储分类字符串,还能保证分类名称修改后,所有关联记录的显示自动同步:
# models.py class Category(models.Model): name = models.CharField(verbose_name="分类名称") def __str__(self): return self.name class ObjectCategory(models.Model): name_object = models.IntegerField(verbose_name="Наименование объекта") category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="关联分类") class CreateNewGPR(models.Model): # 用ForeignKey关联分类模型,自动生成choices name_category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="Наименование категории/раздела/вида работ") # 其他字段保持不变...
这种情况下,Django会自动为name_category生成符合要求的choices,不需要手动处理QuerySet转换。
四、完整的修正后视图代码片段
# views.py def book(request, pk): context = {} form_category_1 = Category_pk_for_form_category_Form() # 初始化表单并动态设置choices form_2 = Form_GPR() # 生成带默认空选项的分类choices category_choices = list(Category_pk_for_form_category.objects.filter(name_object=pk).values_list("category_name", "category_name").distinct()) category_choices.insert(0, ("", "请选择分类")) form_2.fields["name_category"].choices = category_choices if request.method == 'POST': if request.POST.get("form_type") == "form1": form_category_1 = Category_pk_for_form_category_Form(request.POST) if form_category_1.is_valid(): model_instance = form_category_1.save(commit=False) model_instance.name_object = pk model_instance.save() return redirect("book-detail", pk) elif request.POST.get("form_type") == "form2": form_2 = Form_GPR(request.POST) if form_2.is_valid(): model_instance = form_2.save(commit=False) model_instance.name_object = pk model_instance.save() return redirect("book-detail", pk) # 获取当前对象的GPR记录 book = get_object_or_404(ArkiObject_1, pk=pk) filter_qs = CreateNewGPR.objects.filter(name_object=pk).values( "name_working", "type_izm", "value_work", "lyudi_norma", "technik_norma", "date_begin", "date_end" ) context['books'] = filter_qs context['form_category_1'] = form_category_1 context['form_2'] = form_2 return render(request, 'bookdetail.html', context)
这样修改后,你的name_category单选框就能正确显示从其他模型加载的分类选项了。
内容来源于stack exchange

