网站中的可搜索下拉列表 - Django - 保存到数据库
Searchable Dropdown in Website - Django - Saving to data base
我正在构建一个网站,它的一部分涉及一个下拉菜单,它很容易在 HTML 中由 {{ form.inspection_type }} 调用。这是一个相当大的下拉菜单,一直滚动到您需要 select 的地方很痛苦,所以过去几天我一直在尝试实现一个可搜索的下拉菜单来替换它。我已经尝试了在可搜索下拉列表中找到的每一个库,但没有使用过我的项目的细节。我已经恢复到通过 HTML 来完成它,并且再次经历了大约 40 个不同的示例和迭代并且已经接近了。 NEW 代码是一个可搜索的下拉列表,其中填充了我的 Django 数据库下拉列表。但是,当我单击提交时,它显示没有数据添加到我拥有的列表视图中。下面是有效但没有可搜索下拉列表的旧代码,我还添加了具有可搜索下拉列表但不会提交数据的新代码。如果我遗漏了什么并且您有任何问题,请告诉我。谢谢!
OLD - 有效但没有可搜索的下拉列表
<form method="post" action="">
{{ form.inspection_type }}
<input class="btn btn-primary" value="Submit">
</form>
NEW - 不提交但有可搜索的 drop-do
<form method="post" action="">
<input list="Dealer" name="Dealer">
<datalist class="SmallFormField" name="Dealer" id="Dealer" onChange="submitForm(this.form, true)" onkeypress="gblKeyPress()" onkeydown="gblKeyDown()" onfocus="gblOnFocus('Dealer')" onblur="gblOnBlur()" onClick="gblOnClick('QuoteMgrSearchCriteria', this.form)">
<option value="">Select a Dealer ...</option>
<option value="2919">{{ form.inspection_type }}</option>
<input class="btn btn-primary" value="Submit">
</form>
编辑:
在没有搜索的情况下工作:
不提交搜索:
forms.py
class SheetForm_Building(forms.ModelForm):
class Meta:
model = Sheet_Building
fields = '__all__'
exclude = ['user']
widgets = {
'inspection_type': forms.Select(choices=INSPECTION_TYPE_BI, attrs={'class': 'form-control'}),
'department': forms.Select(choices=DEPARTMENT_BI, attrs={'class': 'form-control'}),
'concern': forms.Select(choices=CONCERN_BI, attrs={'class': 'form-control'}),
'hazard_level': forms.Select(choices=HAZARD_LEVEL_BI, attrs={'class': 'form-control'}),
'building_address': forms.Select(choices=BUILDING_ADDRESS_BI, attrs={'class': 'form-control'}),
'floor': forms.Select(choices=FLOOR_LEVEL_BI, attrs={'class': 'form-control'}),
'location': forms.Select(choices=LOCATION_BI, attrs={'class': 'form-control'}),
'codes': forms.Select(choices=CODES_BI, attrs={'class': 'form-control'}),
}
models.py:
class Sheet_Building(models.Model):
user = models.ForeignKey(User, default=True, related_name="Building", on_delete=models.PROTECT)
date = models.DateField(blank=True, null=True, verbose_name='Inspection Date')
time = models.TimeField(blank=True, null=True, verbose_name='Inspection Time')
inspection_type = models.CharField(max_length=16, choices=INSPECTION_TYPE_BI, blank=True, null=True, verbose_name='Inspection Type')
flname = models.CharField(max_length=25, blank=True, null=True, verbose_name='Inspector')
report_date = models.DateField(blank=True, null=True, verbose_name='Report Date')
department = models.CharField(max_length=29, choices=DEPARTMENT_BI, blank=True, null=True, verbose_name='Department')
responsible_name = models.CharField(max_length=25, blank=True, null=True, verbose_name='Responsible Person')
building_address = models.CharField(max_length=52, choices=BUILDING_ADDRESS, blank=True, null=True, verbose_name='Building and Address')
floor = models.CharField(max_length=8, choices=FLOOR_LEVEL_BI, blank=True, null=True, verbose_name='Floor / Level')
room = models.CharField(max_length=35, blank=True, null=True, verbose_name='Area / Room')
location = models.CharField(max_length=10, choices=LOCATION_BI, blank=True, null=True, verbose_name='Location')
priority = models.IntegerField(blank=True, null=True, verbose_name='Priority')
hazard_level = models.CharField(max_length=20, choices=HAZARD_LEVEL_BI, blank=True, null=True, verbose_name='Hazard Level')
concern = models.CharField(max_length=31, choices=CONCERN_BI, blank=True, null=True, verbose_name='Concern')
codes = models.CharField(max_length=51, choices=CODES_BI, blank=True, null=True, verbose_name='Element and Code')
correction = models.TextField(max_length=160, blank=True, null=True, verbose_name='Corrective Action')
image = models.ImageField(blank=True, null=True, verbose_name='Image', upload_to='gallery')
notes = models.TextField(max_length=500, blank=True, null=True, verbose_name="Inspector's note")
class Meta:
ordering = ['-pk']
def __str__(self):
return self.flname or 'None'
def get_absolute_url(self):
return reverse('list_building')
编辑2:
这些是我为使您的建议视图更接近一些所做的更改
INSPECTION_TYPE_BI = (
('Building Code', 'Building Code'),
('DEP / EPA', 'DEP / EPA'),
('DEP / OSHA', 'DEP / OSHA'),
('DEP / EPA / OSHA', 'DEP / EPA / OSHA'),
('Electrical Code', 'Electrical Code'),
('Elevator Code', 'Elevator Code'),
('Fire Code', 'Fire Code'),
('Laboratory', 'Laboratory'),
('Life Safety', 'Life Safety'),
('Multi-Media', 'Multi-Media'),
('OSHA', 'OSHA'),
('Playground', 'Playground'),
('Satellite Area', 'Satellite Area'),
('Town of Amherst', 'Town of Amherst'),
('Health', 'Health'),
('Kitchen', 'Kitchen'),
)
def adddata_building(request):
if request.method == "POST":
# To make sure that the option we selected is being sent in the form data
print(request.POST)
form = SheetForm_Building()
optionList = INSPECTION_TYPE_BI[1:]
return render(request, "testapp/layout.html", {
"form": form,
"optionList": optionList
})
这是我通常用来提交的表单,但它没有可搜索的下拉功能。
def adddata_building(response):
if response.method == 'POST':
form = SheetForm_Building(response.POST, response.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user = response.user
instance.save()
response.user.Building.add(instance)
return redirect('list_building')
else:
form = SheetForm_Building()
return render(response, 'sheets/add_data/add_data_building.html', {'form': form})
这就是我目前正在将两者集成在一起的地方,以创建一个带有可搜索下拉列表的表单,该表单也可以提交。这会提交表单和表单中的所有其他字段,但不会提交可搜索的下拉字段。它显示已提交的所有其他字段及其值,但没有显示可搜索下拉字段有任何提交的值...
def adddata_building(response):
if response.method == 'POST':
form = SheetForm_Building(response.POST, response.FILES)
optionList = INSPECTION_TYPE_BI[1:]
if form.is_valid():
instance = form.save(commit=False)
instance.user = response.user
instance.save()
response.user.Building.add(instance)
return redirect('list_building')
else:
form = SheetForm_Building()
optionList = INSPECTION_TYPE_BI[1:]
return render(response, 'sheets/add_data/add_data_building.html', {'form': form, 'optionList': optionList})
好的,我不确定为什么,在表单检查类型中,您只有一些选项有 2 个值。
在 Django 中,当创建 options/choices 列表时,数据作为元组列表 [(a,b), (c,d) ...] 输入,元组中的第一个选项是实际存储的值和第二个选项是下拉列表中的 label/human-readable 部分。你可以看到我在说什么here。这是我注意到的第一个潜在问题;事实上,其中一些只有 2 个值。
我注意到的第二件事是,在新版本中,您的选项值被硬编码为 2919。我认为这可能是问题的主要原因。如果您要打印 request.POST
,您会在打印的 QueryDict 中看到 'Dealer': ['2919']
的键值对。您提交的值可能与列表中的任何内容都不匹配。
所以,我个人建议做一些类似于我之前做过的事情:
<form method="post" action="">
<input list="Dealer" name="Dealer">
<datalist class="SmallFormField" name="Dealer" id="Dealer" onChange="submitForm(this.form, true)" onkeypress="gblKeyPress()" onkeydown="gblKeyDown()" onfocus="gblOnFocus('Dealer')" onblur="gblOnBlur()" onClick="gblOnClick('QuoteMgrSearchCriteria', this.form)">
<option value="">Select a Dealer ...</option>
{% for option in optionList %}
<option value="{{option.0}}">{{ option.1 }}</option>
{% endfor %}
<input class="btn btn-primary" value="Submit">
</form>
只需将您直接定义的任何选项列表传递给模板,并使用模板语言的 for 循环来填充标签中的选项。
希望这对你有用
编辑:
好的,我对您如何生成表单有了更清楚的了解。感谢您提供额外的信息。这就是我会做的。我很确定有人会有更好的方法,但现在这对你有用。
Django 没有用于数据列表的内置小部件,自定义现有小部件文件/创建新小部件对我来说有点太复杂了。所以我要做的是向您展示一种方法,使我之前编写的代码与您当前拥有的代码相匹配。
在views.py中:
from .models import Sheet_Building
from .forms import SheetForm_Building
def YourViewName(request):
if request.method == "POST":
# To make sure that the option we selected is being sent in the form data
print(request.POST)
form = SheetForm_Building()
optionList = Sheet_Building.INSPECTION_TYPE_BI[1:]
return render(request, "testapp/layout.html", {
"form": form,
"optionList": optionList
})
在 html 文件中,我展开了表单字段(如图 here 所示),这样我们就可以在表单中通常出现的位置呈现 dataList,而不是在右侧在开头或结尾。
我们将遍历表单中的所有字段,直到遇到 form.inspection_type
。此时,我们不会使用默认的 Django 输入字段,而是在 html 中定义我们自己的输入字段。所以 {{ field.label_tag }}
将按原样使用,但 {{ field }}
将被数据列表的代码替换。
文件如下所示:
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{% if field == form.inspection_type %}
{{ field.label_tag}}
<input list="id_inspection_type" name="inspection_type">
<datalist name="inspection_type" id="id_inspection_type">
<option value="">Select a Dealer ...</option>
{% for option in optionList %}
<option value="{{option.0}}">{{ option.1 }}</option>
{% endfor %}
</datalist>
{% else %}
{{ field.label_tag }} {{ field }}
{% endif %}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit" value="submit">
</form>
这里是 request.POST 的屏幕截图:
如您所见,在数据列表(电气代码)中选择的选项已通过表单提交,没有任何问题。
编辑 2:
我使用了您在代码中使用的 ID 和名称 (经销商)。使用它的问题在于,在您的模型 Sheet_Building 中,您有一个名为 inspection_type 的字段。您的表单 SheetForm_Building 是从该模型创建的。所以它还有一个字段inspection_type.
当您尝试保存此表单时,它会确保在 POST 请求中提交的值与定义的值相同。
现在你的情况是,当你提交名称为 Dealer 的数据时,formData 得到一个同名的键,这不是你的 instance/model 期待。因此,尽管数据已通过表单提交,但并未映射到任何内容,因为您的模型没有字段 Dealer。这就是为什么即使其他所有内容都已保存,这个特定字段的值为 None.
要解决此问题,请保持 name 属性与您在表单中定义的属性相同。 IE。 inspection_type
<input list="id_inspection_type" name="inspection_type">
<datalist id="id_inspection_type">
我正在构建一个网站,它的一部分涉及一个下拉菜单,它很容易在 HTML 中由 {{ form.inspection_type }} 调用。这是一个相当大的下拉菜单,一直滚动到您需要 select 的地方很痛苦,所以过去几天我一直在尝试实现一个可搜索的下拉菜单来替换它。我已经尝试了在可搜索下拉列表中找到的每一个库,但没有使用过我的项目的细节。我已经恢复到通过 HTML 来完成它,并且再次经历了大约 40 个不同的示例和迭代并且已经接近了。 NEW 代码是一个可搜索的下拉列表,其中填充了我的 Django 数据库下拉列表。但是,当我单击提交时,它显示没有数据添加到我拥有的列表视图中。下面是有效但没有可搜索下拉列表的旧代码,我还添加了具有可搜索下拉列表但不会提交数据的新代码。如果我遗漏了什么并且您有任何问题,请告诉我。谢谢!
OLD - 有效但没有可搜索的下拉列表
<form method="post" action="">
{{ form.inspection_type }}
<input class="btn btn-primary" value="Submit">
</form>
NEW - 不提交但有可搜索的 drop-do
<form method="post" action="">
<input list="Dealer" name="Dealer">
<datalist class="SmallFormField" name="Dealer" id="Dealer" onChange="submitForm(this.form, true)" onkeypress="gblKeyPress()" onkeydown="gblKeyDown()" onfocus="gblOnFocus('Dealer')" onblur="gblOnBlur()" onClick="gblOnClick('QuoteMgrSearchCriteria', this.form)">
<option value="">Select a Dealer ...</option>
<option value="2919">{{ form.inspection_type }}</option>
<input class="btn btn-primary" value="Submit">
</form>
编辑:
在没有搜索的情况下工作:
不提交搜索:
forms.py
class SheetForm_Building(forms.ModelForm):
class Meta:
model = Sheet_Building
fields = '__all__'
exclude = ['user']
widgets = {
'inspection_type': forms.Select(choices=INSPECTION_TYPE_BI, attrs={'class': 'form-control'}),
'department': forms.Select(choices=DEPARTMENT_BI, attrs={'class': 'form-control'}),
'concern': forms.Select(choices=CONCERN_BI, attrs={'class': 'form-control'}),
'hazard_level': forms.Select(choices=HAZARD_LEVEL_BI, attrs={'class': 'form-control'}),
'building_address': forms.Select(choices=BUILDING_ADDRESS_BI, attrs={'class': 'form-control'}),
'floor': forms.Select(choices=FLOOR_LEVEL_BI, attrs={'class': 'form-control'}),
'location': forms.Select(choices=LOCATION_BI, attrs={'class': 'form-control'}),
'codes': forms.Select(choices=CODES_BI, attrs={'class': 'form-control'}),
}
models.py:
class Sheet_Building(models.Model):
user = models.ForeignKey(User, default=True, related_name="Building", on_delete=models.PROTECT)
date = models.DateField(blank=True, null=True, verbose_name='Inspection Date')
time = models.TimeField(blank=True, null=True, verbose_name='Inspection Time')
inspection_type = models.CharField(max_length=16, choices=INSPECTION_TYPE_BI, blank=True, null=True, verbose_name='Inspection Type')
flname = models.CharField(max_length=25, blank=True, null=True, verbose_name='Inspector')
report_date = models.DateField(blank=True, null=True, verbose_name='Report Date')
department = models.CharField(max_length=29, choices=DEPARTMENT_BI, blank=True, null=True, verbose_name='Department')
responsible_name = models.CharField(max_length=25, blank=True, null=True, verbose_name='Responsible Person')
building_address = models.CharField(max_length=52, choices=BUILDING_ADDRESS, blank=True, null=True, verbose_name='Building and Address')
floor = models.CharField(max_length=8, choices=FLOOR_LEVEL_BI, blank=True, null=True, verbose_name='Floor / Level')
room = models.CharField(max_length=35, blank=True, null=True, verbose_name='Area / Room')
location = models.CharField(max_length=10, choices=LOCATION_BI, blank=True, null=True, verbose_name='Location')
priority = models.IntegerField(blank=True, null=True, verbose_name='Priority')
hazard_level = models.CharField(max_length=20, choices=HAZARD_LEVEL_BI, blank=True, null=True, verbose_name='Hazard Level')
concern = models.CharField(max_length=31, choices=CONCERN_BI, blank=True, null=True, verbose_name='Concern')
codes = models.CharField(max_length=51, choices=CODES_BI, blank=True, null=True, verbose_name='Element and Code')
correction = models.TextField(max_length=160, blank=True, null=True, verbose_name='Corrective Action')
image = models.ImageField(blank=True, null=True, verbose_name='Image', upload_to='gallery')
notes = models.TextField(max_length=500, blank=True, null=True, verbose_name="Inspector's note")
class Meta:
ordering = ['-pk']
def __str__(self):
return self.flname or 'None'
def get_absolute_url(self):
return reverse('list_building')
编辑2:
这些是我为使您的建议视图更接近一些所做的更改
INSPECTION_TYPE_BI = (
('Building Code', 'Building Code'),
('DEP / EPA', 'DEP / EPA'),
('DEP / OSHA', 'DEP / OSHA'),
('DEP / EPA / OSHA', 'DEP / EPA / OSHA'),
('Electrical Code', 'Electrical Code'),
('Elevator Code', 'Elevator Code'),
('Fire Code', 'Fire Code'),
('Laboratory', 'Laboratory'),
('Life Safety', 'Life Safety'),
('Multi-Media', 'Multi-Media'),
('OSHA', 'OSHA'),
('Playground', 'Playground'),
('Satellite Area', 'Satellite Area'),
('Town of Amherst', 'Town of Amherst'),
('Health', 'Health'),
('Kitchen', 'Kitchen'),
)
def adddata_building(request):
if request.method == "POST":
# To make sure that the option we selected is being sent in the form data
print(request.POST)
form = SheetForm_Building()
optionList = INSPECTION_TYPE_BI[1:]
return render(request, "testapp/layout.html", {
"form": form,
"optionList": optionList
})
这是我通常用来提交的表单,但它没有可搜索的下拉功能。
def adddata_building(response):
if response.method == 'POST':
form = SheetForm_Building(response.POST, response.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user = response.user
instance.save()
response.user.Building.add(instance)
return redirect('list_building')
else:
form = SheetForm_Building()
return render(response, 'sheets/add_data/add_data_building.html', {'form': form})
这就是我目前正在将两者集成在一起的地方,以创建一个带有可搜索下拉列表的表单,该表单也可以提交。这会提交表单和表单中的所有其他字段,但不会提交可搜索的下拉字段。它显示已提交的所有其他字段及其值,但没有显示可搜索下拉字段有任何提交的值...
def adddata_building(response):
if response.method == 'POST':
form = SheetForm_Building(response.POST, response.FILES)
optionList = INSPECTION_TYPE_BI[1:]
if form.is_valid():
instance = form.save(commit=False)
instance.user = response.user
instance.save()
response.user.Building.add(instance)
return redirect('list_building')
else:
form = SheetForm_Building()
optionList = INSPECTION_TYPE_BI[1:]
return render(response, 'sheets/add_data/add_data_building.html', {'form': form, 'optionList': optionList})
好的,我不确定为什么,在表单检查类型中,您只有一些选项有 2 个值。
在 Django 中,当创建 options/choices 列表时,数据作为元组列表 [(a,b), (c,d) ...] 输入,元组中的第一个选项是实际存储的值和第二个选项是下拉列表中的 label/human-readable 部分。你可以看到我在说什么here。这是我注意到的第一个潜在问题;事实上,其中一些只有 2 个值。
我注意到的第二件事是,在新版本中,您的选项值被硬编码为 2919。我认为这可能是问题的主要原因。如果您要打印 request.POST
,您会在打印的 QueryDict 中看到 'Dealer': ['2919']
的键值对。您提交的值可能与列表中的任何内容都不匹配。
所以,我个人建议做一些类似于我之前做过的事情:
<form method="post" action="">
<input list="Dealer" name="Dealer">
<datalist class="SmallFormField" name="Dealer" id="Dealer" onChange="submitForm(this.form, true)" onkeypress="gblKeyPress()" onkeydown="gblKeyDown()" onfocus="gblOnFocus('Dealer')" onblur="gblOnBlur()" onClick="gblOnClick('QuoteMgrSearchCriteria', this.form)">
<option value="">Select a Dealer ...</option>
{% for option in optionList %}
<option value="{{option.0}}">{{ option.1 }}</option>
{% endfor %}
<input class="btn btn-primary" value="Submit">
</form>
只需将您直接定义的任何选项列表传递给模板,并使用模板语言的 for 循环来填充标签中的选项。
希望这对你有用
编辑:
好的,我对您如何生成表单有了更清楚的了解。感谢您提供额外的信息。这就是我会做的。我很确定有人会有更好的方法,但现在这对你有用。
Django 没有用于数据列表的内置小部件,自定义现有小部件文件/创建新小部件对我来说有点太复杂了。所以我要做的是向您展示一种方法,使我之前编写的代码与您当前拥有的代码相匹配。
在views.py中:
from .models import Sheet_Building
from .forms import SheetForm_Building
def YourViewName(request):
if request.method == "POST":
# To make sure that the option we selected is being sent in the form data
print(request.POST)
form = SheetForm_Building()
optionList = Sheet_Building.INSPECTION_TYPE_BI[1:]
return render(request, "testapp/layout.html", {
"form": form,
"optionList": optionList
})
在 html 文件中,我展开了表单字段(如图 here 所示),这样我们就可以在表单中通常出现的位置呈现 dataList,而不是在右侧在开头或结尾。
我们将遍历表单中的所有字段,直到遇到 form.inspection_type
。此时,我们不会使用默认的 Django 输入字段,而是在 html 中定义我们自己的输入字段。所以 {{ field.label_tag }}
将按原样使用,但 {{ field }}
将被数据列表的代码替换。
文件如下所示:
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{% if field == form.inspection_type %}
{{ field.label_tag}}
<input list="id_inspection_type" name="inspection_type">
<datalist name="inspection_type" id="id_inspection_type">
<option value="">Select a Dealer ...</option>
{% for option in optionList %}
<option value="{{option.0}}">{{ option.1 }}</option>
{% endfor %}
</datalist>
{% else %}
{{ field.label_tag }} {{ field }}
{% endif %}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit" value="submit">
</form>
这里是 request.POST 的屏幕截图:
如您所见,在数据列表(电气代码)中选择的选项已通过表单提交,没有任何问题。
编辑 2:
我使用了您在代码中使用的 ID 和名称 (经销商)。使用它的问题在于,在您的模型 Sheet_Building 中,您有一个名为 inspection_type 的字段。您的表单 SheetForm_Building 是从该模型创建的。所以它还有一个字段inspection_type.
当您尝试保存此表单时,它会确保在 POST 请求中提交的值与定义的值相同。
现在你的情况是,当你提交名称为 Dealer 的数据时,formData 得到一个同名的键,这不是你的 instance/model 期待。因此,尽管数据已通过表单提交,但并未映射到任何内容,因为您的模型没有字段 Dealer。这就是为什么即使其他所有内容都已保存,这个特定字段的值为 None.
要解决此问题,请保持 name 属性与您在表单中定义的属性相同。 IE。 inspection_type
<input list="id_inspection_type" name="inspection_type">
<datalist id="id_inspection_type">