我有一个带有与外键相关的选择框的表单(例如,类别)。在同一个页面上,我有另一个链接,可以打开一个新页面来添加外键的新实例。添加新实例后,如何更新当前表单以添加新选项,并保留文本字段中的文本(就像管理页面的行为一样)?
下面是我的一些代码片段:
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页面中可用,并且对body字段所做的任何更改都将被保留。
发布于 2017-10-20 09:46:55
我昨天做了这件事的灵感来自django管理员ForeignKey弹出窗口add.my条件是一个商品有好的类别,我可以添加/编辑/删除商品类别在商品的添加/更新视图和结果添加/编辑/删除商品类别将同步到商品的添加/更新view.Here是一个演示,弹出是由layui支持。


如您所见,我可以在不刷新父页面的情况下添加\更改\删除ForeignKey。
首先在接收add_url\update_url\delete_url的ForeignKey中自定义一个新字段:
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中使用你的category的自定义字段:
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 a success.html将由TemplateResponse返回时(正如你在GoodsCategoryAjaxCreateView form_valid中看到的),重点是success.html只是一个脚本,它可以关闭弹出窗口并在父window.here中的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 %}https://stackoverflow.com/questions/46838484
复制相似问题