网站中的可搜索下拉列表 - 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">