Django:admin中的AJAX多域
我想像filter_horizontal一样在admin中显示许多字段,但在用户键入filter字段时填充选项。有很多选择,一次加载它们需要很多时间 我发现,但在我看来,这似乎是一种过分的做法,因为它需要对模型类进行更改,而我所要做的只是替换表单中的每个多个select字段 编写从admin.widgets.FilteredSelectMultiple继承的自定义小部件字段似乎是正确的方法。因此,我尝试推出自己的小部件:Django:admin中的AJAX多域,ajax,django,django-admin,manytomanyfield,Ajax,Django,Django Admin,Manytomanyfield,我想像filter_horizontal一样在admin中显示许多字段,但在用户键入filter字段时填充选项。有很多选择,一次加载它们需要很多时间 我发现,但在我看来,这似乎是一种过分的做法,因为它需要对模型类进行更改,而我所要做的只是替换表单中的每个多个select字段 编写从admin.widgets.FilteredSelectMultiple继承的自定义小部件字段似乎是正确的方法。因此,我尝试推出自己的小部件: class MultiSelectWidget(FilteredSelec
class MultiSelectWidget(FilteredSelectMultiple):
class Media:
# here should be some js to load options dynamically
js = (
"some_js_to_load_ajax_options.js",
)
def render_options(self, choices, selected_choices):
# this initializes the multiple select without any options
choices = [c for c in self.choices if str(c[0]) in selected_choices]
self.choices = choices
return super(MultiSelectWidget,
self).render_options([], selected_choices)
class MyAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyAdminForm, self).__init__(*args, **kwargs)
self.fields['m2m_field'].widget = MultiSelectWidget('m2m_field', is_stacked=False)
class Meta:
model = MyModel
class MyAdmin(admin.ModelAdmin):
form = MyAdminForm
正确渲染的
但是我不知道如何实现这个部分。我应该编写自己的jQuery代码片段还是修改admin/media/js附带的SelectFilter2?以前有人去过吗
编辑:
虽然与问题的核心无关,但由于我只想覆盖字段的小部件,较短的方法是使用formfield_覆盖:
我会破解select过滤器,它有一组很好的函数供您使用。我从您的代码开始,使用自定义javascript从photologue照片模型中检索值;请注意,我使用的是grappelli,获取json对象的Django url是硬编码的;此外,我的模型中的字段称为照片:
# urls.py
url(r'^get_json_photos/(?P<query>[\w-]+)/$', 'catalogo.views.get_json_photos', name='get_json_photos'),
# views.py
from photologue.models import Photo
from django.utils import simplejson as json
def get_json_photos(request, query):
photos = Photo.objects.filter(title__icontains=query)[:20]
p = [ {"name":photo.title, "id":photo.id} for photo in photos ]
response = json.dumps(p)
return HttpResponse(response, mimetype="application/json")
# admin.py
from django.conf import settings
from django.contrib.admin.widgets import FilteredSelectMultiple
class MyFilteredSelectMultiple(FilteredSelectMultiple):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/core.js",
settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js",
settings.ADMIN_MEDIA_PREFIX + "js/SelectFilter2.js",
settings.MEDIA_URL + "js/ajax_photo_list.js")
class MyModelMultipleChoiceField(ModelMultipleChoiceField):
def clean(self, value):
return [val for val in value]
class GalleryForm(forms.ModelForm):
photos = MyModelMultipleChoiceField(queryset=Photo.objects.none(), required=False,
widget=MyFilteredSelectMultiple(verbose_name="photos", is_stacked=False))
def __init__(self, *args, **kwargs):
super(GalleryForm, self).__init__(*args, **kwargs)
try:
i = kwargs["instance"]
gallery = Gallery.objects.get(pk=i.pk)
qs = gallery.photos.all()
except:
qs = Photo.objects.none()
self.fields['photos'].queryset = qs
class Meta:
model = Gallery
widgets = {
'photos': MyFilteredSelectMultiple(verbose_name="photos", is_stacked=False)
}
class GalleryAdmin(admin.ModelAdmin):
list_display = ('title', 'date_added', 'photo_count', 'is_public')
list_filter = ['date_added', 'is_public']
date_hierarchy = 'date_added'
prepopulated_fields = {'title_slug': ('title',)}
filter_horizontal = ()
form = GalleryForm
# ajax_photo_list.js
(function($){
$("#id_photos_input").live("keyup", function(){
var querystring = $("#id_photos_input").val();
if (querystring) {
$.ajax ({
type: "GET",
url: "/get_json_photos/"+querystring+"/",
cache: false,
success: function(json) {
if (json) {
var list_from = $("#id_photos_from option").map(function() {
return parseInt($(this).val());
});
var list_to = $("#id_photos_to option").map(function() {
return parseInt($(this).val());
});
for (var pid in json) {
if ($.inArray(json[pid].id, list_from) == -1 && $.inArray(json[pid].id, list_to) == -1) {
$("#id_photos_from").prepend("<option value='"+json[pid].id+"'>"+json[pid].name+"</option>");
}
}
SelectBox.init('id_photos_from');
SelectBox.init('id_photos_to');
}
}
});
}
})
}(django.jQuery));
我正在考虑让它通用化,因为这不是我第一次遇到这个问题,如果用户界面对您有吸引力,您可以在管理中使用
对于m2m,它可能会像您建议的那样工作:
class MyAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ManyToManyField: {'widget': ModelSelect2MultipleWidget},
}
# required to make jquery available to select2
# has to be loaded via Admin class (and not via widget or form class) for correct order in output
class Media:
js = ("ext/js/jquery.min.js",)
Ajax的工作原理是将以下URL模式添加到URL.py:
当然,您也可以提供自己的视图实现,请参阅上面链接的文档
我目前没有将其用于m2m,而是用于反向外键关系,因此我在Django admin中以自定义形式使用它,显式地实例化小部件。因此,如果它不使用formfield_覆盖,那么很长的路就是一个选项。+1对于我添加到ajax支持的selectbox代码selectbox.init'id_photos_from'中的这两行代码;选择box.init'id_photos_'to';代码你的多对多关系是画廊照片吗?在库视图/模板中,您有一张带有multiselect的现场照片?这是否在models.py:class GalleryForms.ModelForm:中?
class MyAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ManyToManyField: {'widget': ModelSelect2MultipleWidget},
}
# required to make jquery available to select2
# has to be loaded via Admin class (and not via widget or form class) for correct order in output
class Media:
js = ("ext/js/jquery.min.js",)
# if using ModelWidget
url(r'^select2/', include('django_select2.urls')),