简体   繁体   English

在创建具有相关模型中不存在的多对多字段选择的 Django 模型对象时,如何防止表单提交?

[英]How to keep a form from submitting when creating a Django model object with many-to-many field selection that is doesn't exist in the related model?

I'm using Django to create a web app.我正在使用 Django 创建一个网络应用程序。 I have multiple Django models with relationships between each other.我有多个相互关联的 Django 模型。 One of my models is called Type and another is called TestEquipment .我的一个模型称为Type ,另一个称为TestEquipment The Type model has a many-to-many relationship with TestEquipment . Type模型与TestEquipment具有TestEquipment关系。

To allow the user to create a new Type , I have an html form and to select which TestEquipment will be associated with that Type I have am using searchable dropdown with "chips" (javascript) which loads all TestEquipment and allows you to search and select multiple objects to add.为了允许用户创建一个新的Type ,我有一个 html 表单并选择哪个TestEquipment将与该Type关联我正在使用带有“chips”(javascript)的可搜索下拉列表,它加载所有TestEquipment并允许您搜索和选择要添加的多个对象。 When the user submits the form, the selected TestEquipment is added.当用户提交表单时,将添加选定的TestEquipment

Everything works great other than when the user enters text and presses enter, instead of selecting from the dropdown, a chip is added with that text.除了用户输入文本并按 Enter 之外,一切都很好,而不是从下拉列表中选择,而是添加了带有该文本的芯片。 When the form submits it tries to add a TestEquipment that doesn't exists.当表单提交时,它会尝试添加一个不存在的TestEquipment

I would like to find a way to either not allow adding an objects that doesn't exist or throw an alert "must select from existing test equipment";我想找到一种方法,要么不允许添加不存在的对象,要么抛出“必须从现有测试设备中选择”的警报; somehow I have to ensure that the form does not submit to my constructor and create the new Type if text is added to the field.不知何故,我必须确保表单不会提交给我的构造函数并在文本添加到字段时创建新Type

I've tried to find an answer to this and absolutely have had no luck.我试图找到答案,但绝对没有运气。 Any help is REALLY appreciated!任何帮助真的很感激!

Django Models code: Django 模型代码:

class TestEquipment(models.Model):
    name = models.CharField(max_length=64, unique=True)
    notes = models.TextField(null=True, blank=True)

    def __str__(self):
        return f"{self.name}"


class Type(models.Model):
    name = models.CharField(max_length=64, unique=True)
    type_folder = models.URLField(null = True, blank = True)
    type_test_equipment = models.ManyToManyField(TestEquipment, blank=True, related_name="type_test_equipment")
    type_notes = models.TextField(null=True, blank=True) 
    test_sheet = models.URLField(null=True, blank=True) 
    type_test_guide = models.URLField(max_length=300, null=True, blank=True)

    objects = TypeManager()

    def __str__(self):
        return f"{self.name}"

Views.py code: Views.py 代码:

def create_type_view(request):

    if not request.user.is_authenticated:
        return render(request, "jobs/login.html", {"message": None})
    test_equipments = TestEquipment.objects.all()
    equipment_types = Type.objects.all()
    #pass in existing types of equipment
    context= {
        "equipment_types": equipment_types,
        "test_equipments": test_equipments
        }
    if request.user.is_authenticated:
        return render(request, "jobs/create_type.html", context)

create_type.html code (pulls in materialize styling and javascript): create_type.html 代码(引入实体化样式和 javascript):

<div> 
  <form id="type_form" class="col s12" action="{% url 'create_type' %}" method="post">
    {% csrf_token %}
    <div class="row">
      
      <div class="col s12">
        <h6 style="color:#808080">Select Type-Specific Test Equipment</h6>
        <div class="row">
          <div class="input-field col s12">
            <div id="test_equipment-chips" class="chips chips-autocomplete">
            </div>
            <input id="test_equipment" name="test_equipment" style="visibility: hidden;" value="">
          </div>
        </div>
      </div>
    </div>
    <div class="row">
      <input id="submit-btn" type="submit" value="Confirm Add" onclick="onSubmit();" />
    </div>
  </form>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>

<script>
  

  function onSubmit() {
    var chipInstance = M.Chips.getInstance($("#test_equipment-chips"));
    var value = Array.from(chipInstance.chipsData.map(x=>x.tag)).join(",");
    $("#test_equipment").val(value);
    

  }
 
  $('#test_equipment-chips').chips({
    autocompleteOptions: {
      data: {
          {% for test_equipment in test_equipments %}
            '{{test_equipment}}': null,
          {% endfor %}
        },
    limit: Infinity,
    minLength: 0,
      }
    });

</script>

code for constructor:构造函数的代码:

def create_type(request):

    #extract type name from form
    type_name = request.POST["type_name"]

    #create new type object
    new_type=Type.objects.create_type(type_name)
    #fill in all properties that were submitted in the form
    new_type.type_folder = request.POST["type_folder"]
    new_type.test_sheet = request.POST["test_sheet"]
    new_type.type_test_guide = request.POST["type_test_guide"]
    new_type.type_notes = request.POST["type_notes"]
    
    equipment_list=request.POST["test_equipment"].split(",")
    
    for equipment_name in equipment_list:
        
        equipment=TestEquipment.objects.get(name=equipment_name)
        new_type.type_test_equipment.add(equipment.pk)

    #save to database
    new_type.save()

    return HttpResponseRedirect(reverse("jobs"))

So the most important thing to do there is to perform a check in your python because you shouldn't perform a get request to the database like that unless you know it can't fail.所以最重要的事情是在你的 python 中执行检查,因为你不应该像这样对数据库执行get请求,除非你知道它不会失败。 As you're seeing, it just ends up with 500 errors when objects don't exist.如您所见,当对象不存在时,它最终会出现 500 个错误。

So job 1 would be to do this in your view;所以在你看来,工作 1 就是这样做;

    for equipment_name in equipment_list:
        try:
            equipment=TestEquipment.objects.get(name=equipment_name)
        except TestEquipment.DoesNotExist:
            # Log the error somehow if you like?
            print(f"Equipment not found with the name {equipment_name}")
        else:
            # This only executes when the exception wasn't raised
            new_type.type_test_equipment.add(equipment.pk)

Using the above code, it'd just ignore invalid input, which suits a minimum viable option.使用上面的代码,它只会忽略无效输入,这适合最小可行选项。 If you wanted to provide an error in the event of invalid input however you should run through all values in equipment_list to ensure they exist before adding them to the relationship.如果您想在无效输入的情况下提供错误,则您应该在将它们添加到关系之前遍历equipment_list所有值以确保它们存在。 That way you could render the form again with the errors attached.这样您就可以再次渲染带有附加错误的表单。

I've had a look at the demo/docs for chips and it doesn't look like it can help prevent invalid inputs, but you could do some validation in javascript yourself to prevent submission.我已经查看了芯片的演示/文档,它看起来并不能帮助防止无效输入,但是您可以自己在 javascript 中进行一些验证以防止提交。

You should also have a look at using django's forms because your view is directly accessing post data.您还应该看看使用 django 的表单,因为您的视图直接访问发布数据。 By using forms you can handle validation logic outside of your view & there's a bunch more benefits from them.通过使用表单,您可以在视图之外处理验证逻辑,并且它们还有更多好处。 If you're yet to find out about forms, give this page a read;如果您还没有了解表单,请阅读此页面; https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM