Передача kwargs во вложенные формы в встроенных формах Django - PullRequest
0 голосов
/ 12 апреля 2019

Я новичок в Django и пытаюсь внедрить систему отслеживания изменений, которая позволяет пользователям назначать типы изменений, причины и комментарии к любым изменениям, которые происходят в экземплярах элементов класса модели Django.Я смоделировал свою проблему после этого примера https://github.com/philgyford/django-nested-inline-formsets-example У меня есть модель FK-иерархии Item -> ItemHistory -> Item Change Reasons (может быть несколько причин для каждого изменения).Я хотел бы видеть все изменения для данного элемента и все причины и комментарии для всех изменений для этого элемента на той же странице.По типу и причине изменения я реализовал зависимый раскрывающийся список с помощью JQuery - набор запросов по причинам изменения зависит от выбора типа изменения.Проблема, с которой я сталкиваюсь, заключается в отображении сохраненных типов изменений и причин изменений в представлении обновлений.Я пытаюсь передать различные наборы запросов в форму ChangeReasonsForm, которые реализованы как вложенные поля в наборе форм BaseVersionReasonsFormset.Я смог передать наборы запросов из представления в BaseVersionReasonsFormset, но не могу понять, как передать его во вложенные формы.Заранее благодарны за любую помощь и предложения!



Forms.py

class ChangeReasonsForm(forms.ModelForm):
    change_reason = forms.ModelChoiceField(queryset=ChangeReason.objects.none())

    class Meta:
        model = ChangeReasons
        fields = ('change_type_id', 'change_reason', 'comment')

    def __init__(self, *args, **kwargs):
        qs_dict = kwargs.pop('change_reasons')
        index = kwargs.pop('index')
        qs = list(qs_dict.values())[index]
        super(ChangeReasonsForm, self).__init__(*args, **kwargs)

        self.fields['change_reason'].queryset = ChangeReason.objects.none()
        self.fields['change_type_id'].empty_label = ""
        self.fields['change_reason'].empty_label = ""

        self.helper = FormHelper(self)

        if self.instance.pk:
            self.fields['change_reason'].queryset = qs


ChangeReasonsFormSet = inlineformset_factory(
                            ItemHistoryJoiner,
                            ChangeReasons,
                            form=ChangeReasonsForm,
                            extra=1,
                            can_delete=True,
                        )


class BaseVersionReasonsFormset(BaseInlineFormSet):
    """
    The base formset for editing Changes belonging to a Item, and the
    Reasons belonging to those Changes.
    """
    def get_form_kwargs(self, index):
        kwargs = super(BaseVersionReasonsFormset, self).get_form_kwargs(index)
        change_reasons_list = kwargs.pop('change_reasons_list')
        print('index: {}'.format(index))
        print('reasons list: {}'.format(change_reasons_list))
        print('list index i: {}'.format(change_reasons_list[index]))
        self.change_reasons = change_reasons_list[index]
        print('self.change_reasons: {}'.format(self.change_reasons))
        return kwargs

    def add_fields(self, form, index):
        super().add_fields(form, index)
        # Save the formset for a Change's Reasons in the nested property.
        form.nested = ChangeReasonsFormSet(
            instance=form.instance,
            data=form.data if form.is_bound else None,
            files=form.files if form.is_bound else None,
            prefix='change_reason-%s-%s' % (
                form.prefix,
                ChangeReasonsFormSet.get_default_prefix()
            ),
            form_kwargs={'change_reasons': self.change_reasons,
                         'index': index,
                        },
        )

    def is_valid(self):
        """
        Also validate the nested formsets.
        """
        result = super().is_valid()

        if self.is_bound:
            for form in self.forms:
                if hasattr(form, 'nested'):
                    result = result and form.nested.is_valid()

        return result

    def clean(self):
        """
        If a parent form has no data, but its nested forms do, we should
        return an error, because we can't save the parent.
        For example, if the Change form is empty, but there are Reasons.
        """
        super().clean()

        for form in self.forms:
            if not hasattr(form, 'nested') or self._should_delete_form(form):
                continue

            if self._is_adding_nested_inlines_to_empty_form(form):
                form.add_error(
                    field=None,
                    error=('You are trying to add reason(s) to a change which '
                           'does not yet exist. Please add information '
                           'about the change and specify the reason(s) again.'))

    def save(self, commit=True):
        """
        Also save the nested formsets.
        """
        result = super().save(commit=commit)

        for form in self.forms:
            if hasattr(form, 'nested'):
                if not self._should_delete_form(form):
                    form.nested.save(commit=commit)

        return result

    def _is_adding_nested_inlines_to_empty_form(self, form):
        """
        Are we trying to add data in nested inlines to a form that has no data?
        e.g. Adding reasons to a new version whose data we haven't entered?
        """
        if not hasattr(form, 'nested'):
            # A basic form; it has no nested forms to check.
            return False

        if is_form_persisted(form):
            # We're editing (not adding) an existing model.
            return False

        if not is_empty_form(form):
            # The form has errors, or it contains valid data.
            return False

        # All the inline forms that aren't being deleted:
        non_deleted_forms = set(form.nested.forms).difference(
            set(form.nested.deleted_forms)
        )
        # At this point we know that the "form" is empty.
        # In all the inline forms that aren't being deleted, are there any that
        # contain data? Return True if so.
        return any(not is_empty_form(nested_form) for nested_form in non_deleted_forms)


# This is the formset for the versions belonging to a  Item and the
# reasons belonging to those versions.
# You'd use this by passing in a  Item:

VersionsWithCommentsFormset = inlineformset_factory(
                                        Item,
                                        ItemHistory,
                                        formset=BaseVersionReasonsFormset,
                                        # We need to specify at least one ItemHistory field:
                                        fields=('version',),
                                        widgets={
                                            'version': forms.HiddenInput()
                                        },
                                        extra=0,
                                        max_num=0,
                                        # If you don't want to be able to delete  Items:
                                        can_delete=False,
                                )


views.py

class ChangesUpdateView(SingleObjectMixin, FormView):
    """
    For assigning reasons and comments to changes
    """
    model = Item
    template_name = 'app/Item_changes_update.html'

    def get(self, request, *args, **kwargs):
        # The Item we're editing:
        self.object = self.get_object(queryset=Item.objects.all())
        return super().get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        # The Publisher we're uploading for:
        self.object = self.get_object(queryset=Item.objects.all())
        return super().post(request, *args, **kwargs)

    def get_form(self, form_class=None):
        """
        Use our big formset of formsets, and pass in the Item object.
        """
        item = self.object
        item_history_set = item.history_set.all()
        version_reasons_dict = OrderedDict({'version_id_{}'.format(version.version_id):
                                                {'comment_id_{}'.format(
                                                    comment.pk): comment.change_type_id.change_reasons.all()
                                                 for comment in version.reasons.all()
                                                 }
                                            for version in item_history_set
                                            })
        version_reasons_list = list(version_reasons_dict.values())
        version_comment_formset = VersionsWithCommentsFormset(
            **self.get_form_kwargs(),
            instance=self.object,
            form_kwargs={'change_reasons_list': version_reasons_list}
        )

        return version_comment_formset

    def form_valid(self, form):
        """
        If the form is valid, redirect to the supplied URL.
        """
        # if self.request.POST.get('submit'):
        if form.is_valid():
            form.save()
            messages.add_message(
                self.request,
                messages.SUCCESS,
                'Changes were successfully saved.'
            )
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('app:change_detail', kwargs={'pk': self.object.pk})

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the historical versions
        context['item_history'] = get_item_history(self.object)
        return context


...