Как предотвратить отправку формы при создании объекта модели Django с выбором поля «многие ко многим», который не существует в связанной модели?

#javascript #python #html #django

#javascript #питон #HTML #джанго

Вопрос:

Я использую Django для создания веб-приложения. У меня есть несколько моделей Django со связями друг с другом. Одна из моих моделей вызывается Type , а другая вызывается TestEquipment . Type Модель имеет отношение «многие ко многим» с TestEquipment .

Чтобы позволить пользователю создавать новую Type , у меня есть HTML-форма, и для выбора того, что TestEquipment будет связано с этим Type , я использую выпадающий список с возможностью поиска с «фишками» (javascript), который загружает все TestEquipment и позволяет вам искать и выбирать несколько объектов для добавления. Когда пользователь отправляет форму, добавляется выбранное TestEquipment .

Все работает отлично, за исключением того, что когда пользователь вводит текст и нажимает enter, вместо выбора из выпадающего списка добавляется чип с этим текстом. Когда форма отправляет, она пытается добавить TestEquipment , которого не существует.

Я хотел бы найти способ либо запретить добавление объектов, которые не существуют, либо выдать предупреждение «необходимо выбрать из существующего тестового оборудования»; каким-то образом я должен убедиться, что форма не отправляется моему конструктору, и создать новый, Type если в поле добавлен текст.

Я пытался найти ответ на этот вопрос, и мне абсолютно не повезло. Любая помощь ДЕЙСТВИТЕЛЬНО ценится!

Код моделей 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 код:

 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 код (использует стиль materialize и 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>
  

код для конструктора:

 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"))
  

Ответ №1:

Итак, самое важное, что нужно сделать, это выполнить проверку в вашем python, потому что вам не следует выполнять get запрос к базе данных подобным образом, если вы не знаете, что он не может завершиться ошибкой. Как вы видите, он просто выдает 500 ошибок, когда объекты не существуют.

Итак, задание 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)
  

Используя приведенный выше код, он просто игнорирует недопустимый ввод, что соответствует минимально приемлемому варианту. Однако, если вы хотите выдать ошибку в случае неверного ввода, вам следует просмотреть все значения в equipment_list , чтобы убедиться, что они существуют, прежде чем добавлять их к взаимосвязи. Таким образом, вы могли бы снова отобразить форму с прикрепленными ошибками.

Я просмотрел демонстрацию / документы для чипов, и не похоже, что это может помочь предотвратить недопустимые входные данные, но вы могли бы выполнить некоторую проверку в javascript самостоятельно, чтобы предотвратить отправку.

Вам также следует взглянуть на использование форм django, потому что ваше представление напрямую обращается к данным post. Используя формы, вы можете обрабатывать логику проверки вне вашего представления, и от них гораздо больше преимуществ. Если вам еще предстоит узнать о формах, прочитайте эту страницу; https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms

Комментарии:

1. Спасибо, да, это помогло бы устранить ошибки, но все равно создало бы объект нового типа заранее. Мне нужно иметь возможность проверить, все ли объекты, добавленные во входные данные chips, действительны перед отправкой формы и остановить отправку, я предполагаю, что это будет сделано с помощью javascript. Если я это сделаю, мне не понадобится вышеупомянутое, потому что отправка формы уже была остановлена. Вы знаете, как использовать javascript? Я пробовал несколько функций javascript onsubmit, но, думаю, у меня это плохо получается, потому что я не могу в этом разобраться.

2. Вы всегда должны выполнять проверку python, как я показал, потому что вы не можете гарантировать, что у пользователя включен javascript. И если вы хотите очистить объект типа, вы могли бы поместить флаг в блок исключений, который становится true. Затем вне этого цикла, если флаг ошибки имеет значение true, вы удаляете тип вместо сохранения. Однако поместите все это в форму Django, и вы сможете проверить значения, прежде чем перейти к просмотру, и это станет намного проще.

3. Хороший момент для проверки python. Я добавил это. Изначально я рассматривал возможность использования форм Django, но мне совсем не нравится формат, и мне особенно не нравится селектор для поля «многие ко многим». Насколько я читал онлайн, для настройки этого требуется некоторое углубленное редактирование исходного кода Django. Я пошел другим путем и предпочел бы не начинать все сначала с Django forms, если есть способ проверки без них. Мне интересно импортировать ValidationError из django.core.exceptions в файл views или constructors и использовать его без использования их форм. Есть мысли по этому поводу?