Django встроенный набор форм всегда будет создавать новый объект, а не обновлять его - PullRequest
0 голосов
/ 29 мая 2020

У меня 2 модели First и Second с FK от Second до First. Я создал форму для класса 2 и встроенный набор форм для Second. В шаблоне я вручную создал свою форму и с помощью jQuery я могу добавить динамические c формы Second.

В UpdateView форма заполняется правильно, но когда я отправляю форму, все Second экземпляры создаются снова с новыми идентификаторами вместо их обновления. Я дважды проверил, что на HTML есть name = PREFIX-FORM_COUNT-id с правильными идентификаторами, но кажется, что Django игнорирует его.

Я использую Django 2.2.12 & Python 3.6

Вот что я сделал:

models.py

class First(models.Model):
    name = models.CharField(max_length=100, null=False)

class Second(models.Model):
    first= models.ForeignKey(First, null=False, on_delete=models.CASCADE)
    number= models.FloatField(null=False, default=0)

form.py

class FirstForm(forms.ModelForm):

    class Meta:
        model = First
        fields = "__all__"


class SecondForm(forms.ModelForm):
    class Meta:
        model = Second
        fields = "__all__"


SecondsFormset = inlineformset_factory(First, Second, SecondForm)

view.py

class FirstUpdateView(UpdateView):
    template_name = "first.html"
    model = First
    form_class = FirstForm
    context_object_name = "first_obj"

    def get_success_url(self):
        return reverse(...)

    def forms_valid(self, first, seconds):
        try:
            first.save()
            seconds.save()
            messages.success(self.request, "OK!")

        except DatabaseError as err:
            print(err)
            messages.error(self.request, "Ooops!")
        return HttpResponseRedirect(self.get_success_url())

    def post(self, request, *args, **kwargs):
        first_form = FirstForm(request.POST, instance=self.get_object())
        second_forms = SecondsFormset(request.POST, instance=self.get_object(), prefix="second")
        if first_form .is_valid() and second_forms.is_valid():
            return self.forms_valid(first_form , second_forms)
        ...

. html (добавлены только важные теги)

<form method="post">
    {% csrf_token %}
    <input type="text" id="name" value="{{ first_obj.name }}" name="name" required>
    <input type="hidden" name="second-TOTAL_FORMS" value="0" id="second-TOTAL_FORMS">
    <input type="hidden" name="second-INITIAL_FORMS" value="0" id="second-INITIAL_FORMS">
    <input type="hidden" name="second-MIN_NUM_FORMS" value="0" id="second-MIN_NUM_FORMS">
    <div id="seconds_container">
        {% for s in first_obj.second_set.all %}
            <input type="hidden"  name="second-{{forloop.counter0}}-id" value="{{s.pk}}">
            <input type="hidden"  name="second-{{forloop.counter0}}-first" value="{{first_obj.pk}}">
            <input type="number" min="0" max="10" step="1" value="{{s.number}}" name="second-{{forloop.counter0}}-number" required>
        {% endfor %}
    </div>
    <button class="btn btn-success" type="submit">Update</button>
</form>

Я проверил, как Django создает формы, и он только добавит флажок DELETE, но вся остальная информация правильно сохраняется в набор форм. Когда я сделаю .save(), он создаст новый второй элемент в db вместо их изменения. Что мне не хватает?

1 Ответ

1 голос
/ 30 мая 2020

Я решил это! Я установил TOTAL_FORMS и INITIAL_FORMS с неправильными значениями. Из документов Django:

total_form_count возвращает общее количество форм в этом наборе форм. initial_form_count возвращает количество форм в наборе форм, которые были предварительно заполнены, а также используется для определения количества необходимых форм. Вам, вероятно, никогда не понадобится переопределить любой из этих методов, поэтому, пожалуйста, убедитесь, что вы понимаете, что они делают, прежде чем делать это. Представления:

  • Создать FormSets с дополнительным значением = 0

In HTML:

  • Установить TOTAL_FORMS с количеством строк, которые вы POSTing и изменение его динамически, если динамически добавлять / удалять строки;
  • Установить INITIAL_FORMS с количеством уже заполненных строк (редактирование / удаление) и никогда не изменять это;
  • Чтобы удалить предварительно заполненные строка используйте флажок DELETE вместо удаления всей строки;
...