我有一个带有与外键相关的选择框的表单(例如,类别)。在同一页面上,我有另一个链接打开一个新页面来添加外键的新实例。添加新实例后,如何更新当前表单以添加新选项,并在文本字段中保留文本(就像管理页面的行为一样)?
以下是我的一些代码段:
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页面中显示,并保留对正文字段的任何更改。
答案 0 :(得分:0)
昨天我做了这个启发django admin ForeignKey弹出窗口add.my条件是商品有goodcategory我可以在商品的添加/更新视图中添加/编辑/ delet goodscategory和结果添加/编辑/ delet goodcategory将同步到商品的添加/更新视图。这是一个演示,弹出窗口由layui支持。 正如你所看到的,我可以添加\ change \ delete ForeignKey而不刷新父页面。
首先自定义一个新的Field to 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')),
}
和一个新的goodcategory表单是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时,templatesResponse会返回success.html(你可以在GoodsCategoryAjaxCreateView form_valid中看到),而point.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 %}