将wargs传递给Django内联表单集中的嵌套表单

时间:2019-04-12 17:02:59

标签: django django-forms django-views

我是Django的新手,我正在尝试实现一个更改跟踪系统,该系统允许用户为Django模型类的项目实例上发生的任何更改分配更改类型,原因和注释。在此示例https://github.com/philgyford/django-nested-inline-formsets-example之后,我为问题建模了。我有一个模型FK层次结构,即项目->项目历史->项目更改原因(每次更改可能有多个原因)。我想在同一页面中查看给定项目的所有更改以及该项目的所有更改的所有原因和评论。由于变更类型和原因,我使用JQuery实现了一个依赖下拉列表-变更原因的queryset取决于对变更类型的选择。我面临的问题是在更新视图上显示已保存的更改类型和现有更改的更改原因。我正在尝试将不同的查询集传递给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


0 个答案:

没有答案