コメントでリンクしたブログ投稿に関する回答を作成しました。
フォームに「新規追加」リンクを含める論理的な方法を見つけることから始めました。私が解決した解決策は、機能を持たせたいフォーム ウィジェットのテンプレートを提供することでした。私のフォームは次のようになります。
# core/forms.py
class IntranetForm(ModelForm):
def __init__(self, *args, **kwargs):
super(IntranetForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_class = 'form-horizontal'
# app/forms.py
class ComplaintForm(IntranetForm):
def __init__(self, *args, **kwargs):
super(ComplaintForm, self).__init__(*args, **kwargs)
self.helper.layout = Layout(
Field(
'case',
css_class='input-xlarge',
template='complaints/related_case.html',
),
Field('date_received', css_class = 'input-xlarge'),
Field('stage', css_class = 'input-xlarge'),
Field('tags', css_class = 'input-xlarge'),
Field('team', css_class = 'input-xlarge'),
Field('handler', css_class = 'input-xlarge'),
Field('status', css_class = 'input-xlarge'),
FormActions(
Submit(
'save_changes',
'Save changes',
css_class = "btn-primary"
),
Button(
'cancel',
'Cancel',
onclick = 'history.go(-1);'
),
),
)
class Meta:
model = Complaint
fields = (
'case',
'date_received',
'stage',
'tags',
'team',
'handler',
'status',
)
最初のフィールドにテンプレート パラメータが追加されていることに注意してください。そのテンプレートは次のようになります。
<div id="div_id_case" class="control-group">
<label for="id_case" class="control-label ">Case</label>
<div class="controls">
<select class="input-xlarge select" id="id_case" name="case"></select>
<a href="{% url 'add_case' %}" id="add_id_case" class="add-another btn btn-success" onclick="return showAddAnotherPopup(this);">Add new</a>
</div>
</div>
django がcase_form.html
テンプレートをレンダリングすると、上記の html が関連するフォーム フィールドとして挿入されます。完全なcomplaint_form.html
テンプレートには、ケース フォームを呼び出す JavaScript コードが含まれています。そのテンプレートは次のようになります。
{% extends 'complaints/base_complaint.html' %}
{% load crispy_forms_tags %}
{% block extra_headers %}
{{ form.media }}
{% endblock %}
{% block title %}Register complaint{% endblock %}
{% block heading %}Register complaint{% endblock %}
{% block content %}
{% crispy form %}
<script>
$(document).ready(function() {
$( '.add-another' ).click(function(e) {
e.preventDefault( );
showAddAnotherPopup( $( this ) );
});
});
/* Credit: django.contrib.admin (BSD) */
function showAddAnotherPopup(triggeringLink) {
/*
Pause here with Firebug's script debugger.
*/
var name = triggeringLink.attr( 'id' ).replace(/^add_/, '');
name = id_to_windowname(name);
href = triggeringLink.attr( 'href' );
if (href.indexOf('?') == -1) {
href += '?popup=1';
} else {
href += '&popup=1';
}
href += '&winName=' + name;
var win = window.open(href, name, 'height=800,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function dismissAddAnotherPopup(win, newId, newRepr) {
// newId and newRepr are expected to have previously been escaped by
newId = html_unescape(newId);
newRepr = html_unescape(newRepr);
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem) {
if (elem.nodeName == 'SELECT') {
var o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
o.selected = true;
}
} else {
console.log("Could not get input id for win " + name);
}
win.close();
}
function html_unescape(text) {
// Unescape a string that was escaped using django.utils.html.escape.
text = text.replace(/</g, '');
text = text.replace(/"/g, '"');
text = text.replace(/'/g, "'");
text = text.replace(/&/g, '&');
return text;
}
// IE doesn't accept periods or dashes in the window name, but the element IDs
// we use to generate popup window names may contain them, therefore we map them
// to allowed characters in a reversible way so that we can locate the correct
// element when the popup window is dismissed.
function id_to_windowname(text) {
text = text.replace(/\./g, '__dot__');
text = text.replace(/\-/g, '__dash__');
text = text.replace(/\[/g, '__braceleft__');
text = text.replace(/\]/g, '__braceright__');
return text;
}
function windowname_to_id(text) {
return text;
}
</script>
{% endblock %}
そこにある JavaScript は、リンク先のブログ エントリから完全にコピー アンド ペーストされてCaseCreateView
います。元のページに戻り値を渡すには、そのフォームが必要です。
これを行うには、ケースの詳細ページに次のスクリプトを含めます。これは、保存時に詳細ページにリダイレクトするときにスクリプトを呼び出す前に、フォームが適切に保存されることを意味します。詳細テンプレートは次のようになります。
{% extends 'complaints/base_complaint.html' %}
{% block content %}
<script>
$(document).ready(function() {
opener.dismissAddAnotherPopup( window, "{{ case.pk }}", "{{ case }}" );
});
</script>
{% endblock %}
これにより、レンダリングされるとすぐにページが閉じられます。これは理想的ではないかもしれませんが、たとえば、保存時にフォームがリダイレクトするページをオーバーライドし、それを上記のスクリプトの便利なストアとして使用することで、その動作を変更できます。ケースの主キーと Unicode 表現がどのように返されているかに注目してください。これで、選択した<option>
フィールドが元のフォームに入力され、完了です。