【问题标题】:How to refresh the selection box when new option is added in Django?在 Django 中添加新选项时如何刷新选择框?
【发布时间】:2018-03-31 23:57:56
【问题描述】:

我有一个带有与外键(例如,类别)相关的选择框的表单。在同一页面上,我有另一个链接,它打开一个新页面以添加一个新的外键实例。添加新实例后,如何更新当前表单以添加新选项,并保留文本字段中的文本(就像管理页面的行为方式一样)?

这是我的一些代码sn-ps:

update_post.html:

...
<form method="post" novalidate action='.'>
{% csrf_token %}
    {% include 'base_form.html' with form=form %}
    # button to add a category
    <a href="{% url 'blog:create_category' %}?next={{ request.path }}" class="btn btn-primary" role="button" target="_blank">{% trans "Add category" %}</a>
    <button type="submit" class="btn btn-primary" name="publish" value={% trans 'Publish' %}>{% trans 'Publish' %}</button>
</form>
...

create_category.html:

...
<form action="./{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" method="post" novalidate>
    {% csrf_token %}
    {% include 'base_form.html' with form=form %}
    <button type="submit" class="btn btn-primary" value={% trans 'Create' %}>{% trans 'Create' %}</button>
</form>
...

views.py:

...
class PostUpdate(UpdateView):
    template_name = 'update_post.html'
    success_url = '/'
    model = Post
    fields = ['title', 'body', 'category']

class CategoryCreate(CreateView):
    template_name = 'create_category.html'
    model = Category
    fields = ['name']
    def get_success_url(self):
        if 'next' in self.request.GET:
            return self.request.GET.get('next')
        else:
            return reverse('index')
...

我想要做的是,当添加新类别时,它会立即在 update_post 页面中可用,并且保留对正文字段的任何更改。

【问题讨论】:

  • 你能分享你的代码吗?

标签: django forms foreign-keys


【解决方案1】:

我昨天在 django admin ForeignKey popup window add.my 的启发下做了这个到商品的添加/更新视图。这是一个演示,弹出是由layui 支持的。 如您所见,我可以在不刷新父页面的情况下添加\更改\删除外键。

首先为 ForeignKey 自定义一个新字段,它将接收 add_url\update_url\delete_url:

class ForeignKeyWidget(Select):
    template_name = 'widgets/foreign_key_select.html'

    def __init__(self, url_template, *args, **kw):
        super(ForeignKeyWidget, self).__init__(*args, **kw)
        # Be careful that here "reverse" is not allowed
        self.url_template = url_template

    def get_context(self, name, value, attrs):
        context = super(ForeignKeyWidget, self).get_context(name, value, attrs)
        context['add_url'] = self.url_template
        context['update_url'] = self.url_template
        context['delete_url'] = self.url_template + 'lang_delete/'
        return context

第二个是为您的自定义字段自定义一个小部件,它可以弹出添加/更新类别窗口并使用 ajax 删除类别:

foreign_key_select.html:

{% include "django/forms/widgets/select.html" %}

    <style>
        #{{ widget.attrs.id }}_add, #{{ widget.attrs.id }}_change, #{{ widget.attrs.id }}_delete {
            margin-top: 10px;
            padding: 0 10px;
            height: 25px;
            line-height: 25px;
        }
    </style>

    <a class="layui-btn layui-btn-mini" id="{{ widget.attrs.id }}_add">
        add
    </a>
    <a class="layui-btn layui-btn-mini layui-btn-disabled" id="{{ widget.attrs.id }}_change">
        change
    </a><a class="layui-btn layui-btn-mini layui-btn-disabled" id="{{ widget.attrs.id }}_delete">
        delete
    </a>

    <script>
        $('#{{ widget.attrs.id }}_add').click(function () {
            var index = layui.layer.open({
                title: "add_category",
                type: 2,
                area: ['700px', '500px'],
                content: "{{ add_url }}" + '?popup=1&to_field={{ widget.attrs.id }}',
                success: function (layer, index) {

                }
            });
        });

        $("#{{ widget.attrs.id }}_change").click(function () {
            var id = $('#{{ widget.attrs.id }}').val();
            if (id) {
                var index = layui.layer.open({
                    title: "change_category",
                    type: 2,
                    area: ['700px', '500px'],
                    content: '{{ update_url }}' + id + '?popup=1&to_field={{ widget.attrs.id }}',
                    success: function (layer, index) {

                    }
                });
            }
        });

        $("#{{ widget.attrs.id }}_delete").click(function () {
            var id = $('#{{ widget.attrs.id }}').val();
            var value = $('#{{ widget.attrs.id }} option[value=' + id + ']').text();
            var indexGood = value.lastIndexOf('>');
            var valueN = indexGood > 0 ? value.substring(indexGood + 1, value.length) : value;
            if (id) {
                layer.confirm('corform delete' + valueN + ' ?', {icon: 3, title: 'delete'}, function (index) {
                    $.ajax({
                        type: "POST",
                        data: {},
                        url: '{{ delete_url }}' + id + '/',
                        beforeSend: function (xhr) {
                            xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
                        },
                        success: function (data, textStatus) {
                            layer.close(index);
                            $('#{{ widget.attrs.id }} option[value=' + data.id + ']').remove();
                            $("#{{ widget.attrs.id }}_change,#{{ widget.attrs.id }}_delete").addClass('layui-btn-disabled');

                            return false;
                        },
                        error: function (XMLHttpRequest, textStatus, errorThrown) {
                            layer.alert('delete failed' + XMLHttpRequest.responseText)
                        }
                    });
                });
            }
        });

        function {{ widget.attrs.id }}_isDisabled() {
            if ($('#{{ widget.attrs.id }}').val()) {
                $("#{{ widget.attrs.id }}_change,#{{ widget.attrs.id }}_delete").removeClass('layui-btn-disabled');
            } else {
                $("#{{ widget.attrs.id }}_change,#{{ widget.attrs.id }}_delete").addClass('layui-btn-disabled');
            }
        }

        $('#{{ widget.attrs.id }}').change(function () {
            {{ widget.attrs.id }}_isDisabled();
        });

        {{ widget.attrs.id }}_isDisabled();
    </script>

第三个是在 forms.py 中使用您的自定义字段作为类别:

class GoodsForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(GoodsForm, self).__init__(*args, **kwargs)
        self.fields['category'].widget.attrs.update({'class': 'form-control'})
        self.fields['title'].widget.attrs.update({'class': 'form-control'})
        self.fields['content'].widget.attrs.update({'class': 'form-control'})

    class Meta:
        model = Goods
        fields = ['category', 'title', 'content']
        widgets = {
            'category': ForeignKeyWidget(url_template=reverse_lazy('goods_category_ajax_create')),
        }

一个新的好类别表单是forms.py

class GoodsCategoryForm(TranslatableModelForm):
    def __init__(self, *args, **kwargs):
        super(GoodsCategoryForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'class': 'form-control'})
        self.fields['cover'].widget.attrs.update({'class': 'form-control'})
        self.fields['parent'].widget.attrs.update({'class': 'form-control'})

    class Meta:
        model = GoodsCategory
        fields = ['name', 'cover', 'parent']

四是在你的views.py中处理请求:

class GoodsCategoryAjaxCreateView(BaseContextMixin, IsStaffUserMixin, CreateView):
    form_class = GoodsCategoryForm
    template_name = 'goods_category_ajax/create.html'

    def get_context_data(self, **kwargs):
        if 'to_field' in self.request.GET:
            kwargs['to_field'] = self.request.GET['to_field']
        return super(GoodsCategoryAjaxCreateView, self).get_context_data(**kwargs)

    def form_valid(self, form):
        self.object = form.save()
        context = {'op': 'create', 'id': self.object.id, 'value': self.object.__str__()}
        if 'to_field' in self.request.GET:
            context['to_field'] = self.request.GET['to_field']
        return TemplateResponse(self.request, 'goods_category_ajax/success.html', context=context)


    class GoodsCategoryAjaxUpdateView(BaseContextMixin, IsStaffUserMixin, UpdateView):
        model = GoodsCategory
        form_class = GoodsCategoryForm
        slug_field = 'id'
        context_object_name = 'goods_category'
        template_name = 'goods_category_ajax/update.html'

        def get_context_data(self, **kwargs):
            if 'to_field' in self.request.GET:
                kwargs['to_field'] = self.request.GET['to_field']
            return super(GoodsCategoryAjaxUpdateView, self).get_context_data(**kwargs)

        def form_valid(self, form):
            self.object = form.save()
            context = {'op': 'create', 'id': self.object.id, 'value': self.object.__str__()}
            if 'to_field' in self.request.GET:
                context['update'] = self.request.GET['to_field']
            return TemplateResponse(self.request, 'goods_category_ajax/success.html', context=context)


    class GoodsCategoryAjaxLangDeleteView(BaseContextMixin, IsStaffUserMixin, FakeDeleteView):
        model = GoodsCategory
        slug_field = 'id'

        def delete(self, request, *args, **kwargs):
            self.object = self.get_object()
            data = {'op': 'delete', 'id': self.object.id, 'value': self.object.__str__()}
            self.object.delete()
            return JsonResponse(data=data)

urls.py:

url(r'^ajax/$', GoodsCategoryAjaxCreateView.as_view(), name='goods_category_ajax_create'),
url(r'^ajax/(?P<slug>\d+)/$', GoodsCategoryAjaxUpdateView.as_view(), name='goods_category_ajax_update'),
url(r'^ajax/lang_delete/(?P<slug>\d+)/$', GoodsCategoryAjaxLangDeleteView.as_view(),
    name='goods_category_ajax_lang_delete'),

五是您的添加弹出窗口将通过 GoodsCategoryAjaxCreateView 打开 url 句柄,返回模板为:

{% extends "manage/base.html" %}

{% block main %}

    <form id='goods_category_ajax_create' class="form-horizontal" enctype="multipart/form-data"
          action="{% url 'goods_category_ajax_create' %}{% if to_field %}?to_field={{ to_field }}{% endif %}"
          method="post">
        {% include 'manage/widgets/form.html' %}

        <div class="form-group">
            <div class="col-sm-offset-1 col-sm-10">
                <input class="layui-btn layui-btn-normal" type="submit" value="add_category"/>
            </div>
        </div>
    </form>
{% endblock %}

您使用modelform和createview提交一个新类别,当表单is_vaild时,TemplateResponse将返回一个success.html(正如您在GoodsCategoryAjaxCreateView form_valid中看到的那样),关键是success.html只不过是一个可以关闭弹出窗口并在父窗口中的 to_field 元素中插入新选项。这里是success.html:

{% extends "manage/base.html" %}

{% block main %}
    <script>
        var to_field = '#{{ to_field }}', op = '{{ op }}', id = '{{ id }}', value = '{{ value }}';
        if (to_field) {
            switch (op) {
                case 'create':
                    if (id) {
                        var index = parent.layer.getFrameIndex(window.name); //get current iFrame index
                        parent.layer.close(index); //close
                        $option = '<option value=' + id + ' selected>' + value + '</option>';
                        $(to_field, window.parent.document).append($option);
                        $(to_field + '_change,' + to_field + '_delete', window.parent.document).removeClass('layui-btn-disabled');
                    }
                    break;
                case 'update':
                    if (id) {
                        var index = parent.layer.getFrameIndex(window.name); 
                        parent.layer.close(index); 
                        $(to_field + ' option[value=' + id + ']', window.parent.document).html(value);
                    }
                    break;
            }
        }
    </script>
{% endblock %}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 2021-09-08
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    相关资源
    最近更新 更多