Виджет поля M2M для добавления или обновления данных в виде встроенного набора форм - PullRequest
0 голосов
/ 28 января 2020

Я потратил много часов на поиск функции, которая, на мой взгляд, должна быть c базовой в Django. Но я просто не могу заставить его работать,

Я не могу найти виджет, который будет функционировать так же, как виджет m2m django, но также создаст новый экземпляр модели, если он не существует.

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

Например, если у меня были модели как:

class Outcome(models.Model):
    outcome = models.CharField(max_length=255)
    outcome_short_name = models.CharField(max_length=10, blank=True, null=True)


class Course(models.Model):
    course_title = models.CharField(
        verbose_name=COURSE_SINGULAR + " title", max_length=200, unique=True
    )

    course_outcome = models.ManyToManyField(
        Outcome, verbose_name=COURSE_SINGULAR + " outcome", blank=True
    )

Затем я хочу, чтобы «Результаты» отображались как это изображение при создании курса: Изображение добавления нового курса с встроенными результатами

Теперь, если данные результатов, добавленные пользователем, уже существуют, то это следует только сопоставить их с курсом. В противном случае следует сначала сохранить результаты в базе данных, а затем сопоставить их с курсом.

Любые указания в правильном направлении будут высоко оценены.

Спасибо,

РЕДАКТИРОВАТЬ: Как предложено @dirkgroten для использования modelformset, я изменил свой FormView следующим образом:

class CourseFormView(FormView):
    template_name = "course/course_form.html"
    form_class = CourseForm
    success_url = "/admin/"

    def get_context_data(self, **kwargs):
        context = super(CourseFormView, self).get_context_data(**kwargs)
        if self.request.POST:
            context["outcomes"] = OutcomeFormSet(self.request.POST)
        else:
            context["outcomes"] = OutcomeFormSet(queryset=Outcome.objects.none())
        return context

    def form_valid(self, form, **kwargs):
        super(CourseFormView, self).get_context_data(**kwargs)
        context = self.get_context_data()
        outcomes_formset = context["outcomes"]
        if not outcomes_formset.is_valid():
            return super().form_invalid(form)

        cleaned_data = form.cleaned_data
        cleaned_data.pop("course_outcome")
        course = Course.objects.create(**cleaned_data)
        course.save()

        outcomes_formset.instance = course
        outcomes_formset.save()


course.course_outcome.set(Outcome.objects.filter(course_outcome=course))
    return super().form_valid(form)

Все выглядит хорошо, за исключением того, что мой model_formset не проверяется, если данные формы в formset уже существуют в база данных.
Например, если я ввожу (output = "test_outcome", output_short_name = "test_short") в набор форм, и те же данные уже существуют в таблице результатов, тогда мой набор форм выдает ошибку:
Результат с этим коротким именем Outcome и Outcome уже существует.

Есть ли способ справиться с этой ситуацией, или я делаю что-то не так.

Вы можете проверить выше по адресу: http://code.gdy.club: 8001 / course / add /
список результатов: http://code.gdy.club: 8001 / результат /

Спасибо,

-
Сурадж
https://hacksj4u.wordpress.com
https://github.com/SurajDadral

1 Ответ

0 голосов
/ 28 января 2020

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

Вы можете сделать это на OutcomeFormset s clean() метод, подобный следующему:

from django.core.exceptions import NON_FIELD_ERRORS

def clean(self):
    super().clean()
    for i in range(0, self.total_form_count()):
        form = self.forms[i]
        if form.non_field_errors() and len(form.errors) == 1:
            # the only error is probably due to unique_together constraint
            try:
                form.instance = Outcome.objects.get(
                    outcome=form.data.get(form.add_prefix('outcome')),
                    outcome_short_name=form.data.get(form.add_prefix('outcome_short_name')))
            except Outcome.DoesNotExist:
                pass  # some other error so we should keep it
            else:
                del form._errors[NON_FIELD_ERRORS]

Тогда, по вашему мнению, при сохранении набора форм вы должны l oop через все формы, а не сохранять те, в которых экземпляр имеет pk:

for form in outcomes_formset:
    outcome = form.instance    
    if not outcome.pk:
        outcome = form.save()
    course.course_outcome.add(outcome)
...