2011-02-13 9 views
4

ManyToManyFieldfilter_horizontal처럼 admin으로 표시하고 사용자가 필터 필드에 입력 할 때 옵션을 채 웁니다. 많은 옵션이 있으며 한꺼번에로드하는 데 많은 시간이 걸립니다.Django : AJAX ManyToManyField in admin

나는 django-ajax-filtered-fields을 찾았지만, 모델 클래스를 변경해야하기 때문에 잔인한 것처럼 보입니다. 폼에서 여러 개의 선택 필드를 모두 바꿔야한다는 것입니다.

admin.widgets.FilteredSelectMultiple에서 상속받은 맞춤 위젯 필드를 작성하는 것이 올바른 방법입니다. 그래서 내 자신의 위젯을 굴리려하고 있습니다 :

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 

올바르게 렌더링됩니다.

그러나 이것을 구현하는 방법을 모르겠다. some_js_to_load_ajax_options.js 아약스 부분. 나 자신의 jQuery 스 니펫을 작성하거나 admin/media/js과 함께 제공되는 SelectFilter2을 수정해야합니까? 누구도 전에 거기에 있었 니?

편집 : , 문제의 핵심에 관련된 난 단지 필드의 위젯을 대체하려면로, 짧은 방법은 formfield_overrides을 사용하는 것은 아니지만 :

class MultiSelectWidget(FilteredSelectMultiple): 
    # as above 

class MyAdmin(admin.ModelAdmin): 
    formfield_overrides = { 
     models.ManyToManyField: {'widget': MultiSelectWidget}, 
    } 

답변

0

내가 선택 필터를 해킹 것 , 그것은 당신이 사용할 수있는 멋진 기능들을 가지고 있습니다.

+1

당신은 더 구체적 일 수 있습니까? – omat

4

나는 코드에서 시작했고 photologue Photo 모델에서 값을 검색하기 위해 사용자 정의 javascript를 사용했습니다. 내가 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)); 

나는이 문제가 처음이 아니기 때문에, 그것은 일반적인하게 생각하고,

+0

+ +이 두 줄에 +1은 내 아약스 지원 선택 상자'code'SelectBox.init ('id_photos_from'); SelectBox.init ('id_photos_to');'code' – elsadek

+0

당신의 many to many 관계는 gallery-photo입니까? 갤러리보기/템플릿에서 멀티 셀렉트가있는 "사진"필드가 있습니까? 이게 models.py : class GalleryForm (forms.ModelForm) :입니까? – Timo

0

만약 Select2 항소의 UI 관리자에게 Django-Select2을 사용할 수 있습니다. 당신처럼 작동 할 수 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",) 

아약스 urls.py에 다음 URL 패턴을 추가하여 작동 : 물론

# if using ModelWidget 
url(r'^select2/', include('django_select2.urls')), 

, 당신은 또한 당신의 자신의 뷰 구현을 제공 할 수 있습니다, 위에 링크 된 문서를 참조하십시오.

저는 현재 m2m 대신에 외래 키 관계를 사용하고 있지 않으므로 장고 관리자의 사용자 정의 양식에서 위젯을 명시 적으로 인스턴스화하고 있습니다. 따라서 formfield_overrides으로 작동하지 않는 경우 긴 방법이 옵션 일 수 있습니다.