Django: AJAX ManyToManyField in admin

后端 未结 3 1880
礼貌的吻别
礼貌的吻别 2021-02-10 03:19

I want to display ManyToManyFields in admin just like filter_horizontal does, but populate the options as the user types into the filter field. There a

相关标签:
3条回答
  • 2021-02-10 03:33

    If the UI of Select2 appeals to you, you could use Django-Select2 in the Admin.

    For m2m it might work like you suggested:

    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 works by adding the following URL pattern to urls.py:

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

    Of course, you can also provide your own view implementations, see the documentation linked above.

    I'm currently not using it for m2m but for reverse foreign key relations, so I'm using it in a custom form in the Django admin, instantiating the widget explicitly. Thus, in case it's not working with formfield_overrides, the long way would be an option.

    0 讨论(0)
  • 2021-02-10 03:37

    I started from your code and I used a custom javascript to retrieve values from photologue Photo model; please note that I'm using grappelli and the Django url that get the json object is hardcoded; also the field in my model is called "photos":

    # 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));
    

    I'm thinking to make it generic, since is not the first time that I have this problem,

    0 讨论(0)
  • 2021-02-10 03:38

    I would hack the select filter, it has a nice set of functions that you can use.

    0 讨论(0)
提交回复
热议问题