Чекбоксы в Django и JS не работают должным образом

У меня серьезная проблема с чекбоксами. Я создаю приложение в стиле ToDo и хочу, чтобы оно сохраняло изменения (item_done = False или True) при нажатии на чекбокс.

Я реализовал JS-скрипт, чтобы все выходило "гладко", я использую jQuery. К сожалению, возникает следующая ошибка: при нажатии на первый элемент в списке все работает плавно, но при нажатии на второй изменение происходит в первом и наоборот, при нажатии на первый изменение происходит во втором.

Я не могу понять, что я делаю не так.

Я включил код ниже.

HTML с Django, список элементов в задании:

<div class="card-header bg-transparent" ><h3>To-do list</h3></div>
    <div class="text-start">
      <br/>
    <ul class="list-group">
    {% if list_items %}
      {% for item in list_items %}
        {% if item.item_done %}
        <li class="list-group-item">
          <input class="form-check-input" type="checkbox" checked="checked" value="{{ item.id }}" id="un-check-box">
          {{ item.title }}
        </li>
        {% else %}
        <li class="list-group-item">
          <input class="form-check-input" type="checkbox" value="{{ item.id }}" id="check-box">
          {{ item.title }}
        </li>
        {% endif %}

      {% endfor %}
    {% else %}
      <p class="text-center">the task list is empty</p>
    {% endif %}
    </ul>

JS-код:

   <script>
// Check if checkbox pressed
$(document).on('click', '#check-box', function(e){
  e.preventDefault();
  $.ajax({
    type: 'POST',
    url: '{% url 'check_boxes' %}',
    data: {
      item_id: $('#check-box').val(),
      csrfmiddlewaretoken: '{{ csrf_token }}',
      action: 'post'
    },

    success: function(json){
      console.log(json)
      document.getElementById("check-box").checked = true;
      location.reload();
    },

    error: function(xhr, errmsg, err){

    }

  });
});

// Uncheck boxes
$(document).on('click', '#un-check-box', function(e){
  e.preventDefault();
  $.ajax({
    type: 'POST',
    url: '{% url 'uncheck_boxes' %}',
    data: {
      item_id: $('#un-check-box').val(),
      csrfmiddlewaretoken: '{{ csrf_token }}',
      action: 'post'
    },

    success: function(json){
      console.log(json)
      document.getElementById("un-check-box").checked = false;
      location.reload();
    },

    error: function(xhr, errmsg, err){

    }

  });
});


</script>

Виды Python:

def check_boxes(request):
    if request.POST.get('action') == 'post':
        item_id = int(request.POST.get('item_id'))
        item = get_object_or_404(ToDoItem, id=item_id)
        item.item_done = True
        item.save()
       
        response = JsonResponse({'item': item.item_done,
                                 'item_id' : item_id
                                 })
    return response

def uncheck_boxes(request):
    if request.POST.get('action') == 'post':
        item_id = int(request.POST.get('item_id'))
        item = get_object_or_404(ToDoItem, id=item_id)
        item.item_done = False
        item.save()
        print(item.item_done)

        response = JsonResponse({'item': item.item_done,
                                 'item_id': item_id
                                 })
    return response

Модель задачи и ToDoItem для понимания остального кода.

class Task(models.Model):
    task_user = models.ForeignKey(AppUser, on_delete=models.CASCADE)
    title = models.CharField(max_length=50)
    description = models.TextField(max_length=1000)
    date = models.DateField(default=datetime.datetime.today)
    image = models.ImageField(upload_to='uploads/task/', blank=True)
    is_done = models.BooleanField(default=False)

    def __str_(self):
        """ return object name"""
        return self.title

class ToDoItem(models.Model):
    title = models.CharField(max_length=100)
    task = models.ForeignKey(Task, on_delete=models.CASCADE)
    item_done = models.BooleanField(default=False)

    def __str__(self):
        """return object name"""
        return self.title

Пожалуйста, помогите мне, я хочу понять, что я делаю не так и как я могу это исправить.

Вы создаете на странице несколько элементов с одинаковым id (либо 'check-box', либо 'un-check-box'), что, скорее всего, и является причиной вашей проблемы. Вероятно, вы хотите присвоить элементам определенный класс, а затем использовать document.getElementsByClassName для их перебора. О, вы используете jQuery... я помню, что это $('.class-name').each()

В любом случае присвойте каждому элементу уникальный ID или не присваивайте его. Надеюсь, это поможет!

Я сделал несколько изменений. В представлениях я сделал логику, которая обрабатывает, является ли item_done истиной или ложью.

def check_boxes(request):
    if request.POST.get('action') == 'post':
        item_id = int(request.POST.get('item_id'))
        item = get_object_or_404(ToDoItem, id=item_id)
        if item.item_done:
            item.item_done = False
            item.save()
        else:
            item.item_done = True
            item.save()

        response = JsonResponse({'item': item.item_done,
                                 'item_id': item_id
                                 })
    return response

В html a изменилось:

<ul class="list-group">
{% if list_items %}
  {% for item in list_items %}
    <li class="list-group-item">
      <input type="checkbox" {{ item.item_done|yesno:"checked," }} value="{{ item.id }}"
             class="check-box" id="{{forloop.counter}}">
      {{ item.title }}
    </li>
  {% endfor %}
{% else %}
  <p class="text-center">the task list is empty</p>
{% endif %}
</ul>

И jQuery:

// check boxes
$('.check-box').on('click', function(e){
  e.preventDefault();
  $.ajax({
    type: 'POST',
    url: '{% url 'check_boxes' %}',
    data: {
      item_id: $(this).val(),
      csrfmiddlewaretoken: '{{ csrf_token }}',
      action: 'post'
    },

    success: function(json){
      console.log(json)
      let checkBox
      //bellow: Uncaught TypeError: checkBox is null
      checkBox = document.getElementById("{{forloop.counter}}")
      if (checkBox.checked === true){
        checkBox.checked = false;
        } else {
        checkBox.checked = true;
        }
      location.reload();

    },

    error: function(xhr, errmsg, err){

    }

  });
});

теперь программа правильно отмечает, если элемент выполнен из базы данных, но не считывает уникальный id, сгенерированный {{forloop.counter}}.

Он воспринимает его как пустой (я добавил ошибку в комментарий JS). Я уже искал решение этой проблемы, но так и не нашел - многие рекомендуют использовать {{forloop.counter}}, но это просто не работает.

Если я использую {{forloop.counter}} с некоторым текстом "checkbox-{{forloop.counter}}" JS читает только {{checkbox}} и обрабатывает каждый элемент с тем же ID (это та же проблема, что и в предыдущем случае).

Что я пропустил в коде?

Я нашел решение.

Может, это и не профи (а может, и профи), но это работает и решило немало моих проблем.

По какой-то причине JS не читал ни {{foorloop.counter}}, ни id, и я зависел от выбора и отмены выбора конкретного чекбокса.

Потом я понял, что item_id передается в json и отображается в контрольном журнале, поэтому я передал значение item в дополнение к item id, а затем извлек его из json с помощью метода getElementById. Далее условие if else и флажки на странице работают так, как я хотел. Я также протестировал в другом браузере, удалил историю и повторил - все работает!

Ниже приведены изменения:

HTML и теги Django:

    <li class="list-group-item">
      <input class="form-check-input me-1 check-box" type="checkbox" {{ item.item_done|yesno:"checked," }} value="{{ item.id }}"
        id="{{ item.id }}">
      {{ item.title }}
    </li>

jQuery

$('.check-box').on('click', function(e){
  e.preventDefault();
  $.ajax({
    type: 'POST',
    url: '{% url 'check_boxes' %}',
    data: {
      item_id: $(this).val(),
      csrfmiddlewaretoken: '{{ csrf_token }}',
      action: 'post'
    },

    success: function(json){
      console.log(json)

      let itemId
      itemId = json.item_id
      let checkBox
      checkBox = document.getElementById(itemId)

      if (checkBox.checked === true){
      checkBox.checked = false;
        } else {
      checkBox.checked = true;
        };

      location.reload();

    },

    error: function(xhr, errmsg, err){

    }

  });

});

view

def check_boxes(request):
    if request.POST.get('action') == 'post':
        item_id = int(request.POST.get('item_id'))
        item = get_object_or_404(ToDoItem, id=item_id)
        if item.item_done:
            item.item_done = False
            item.save()
        else:
            item.item_done = True
            item.save()

        response = JsonResponse({'item': item.item_done,
                                 'item_id': item_id
                                 })
    return response
Вернуться на верх