【问题标题】:Tom-Select as Django WidgetTom-Select 作为 Django 小部件
【发布时间】:2021-09-03 18:13:52
【问题描述】:

我想使用tom-select 作为 Django 自动完成选择小部件。

对应的数据库表(Runners)有几百行,所以无法全部加载到html页面中。

在模型RaceTeam 中,我有一个指向Runner 的ForeignKey。

选择小部件将用于整个页面和 html 片段(通过htmx)。

我尝试了子类django.forms.Select,但这会将所有数据库行吸入widget.choices

【问题讨论】:

    标签: javascript django autocomplete htmx


    【解决方案1】:

    我使用这个解决方案:

    我将 tom-select JS+CSS 复制到 static/relayrace/vendor/。

    class RunnerSelectWidget(Select):
        class Media:
            css = {
                'all': ('relayrace/vendor/tom-select.css',)
            }
            js = ('relayrace/vendor/tom-select.complete.min.js',)
    
        def set_choices(self, value):
            # there can be several thousand choices. We don't want them here
            self._choices = []
    
        def get_choices(self):
            choices = list(self._choices)
            return choices
    
        choices = property(get_choices, set_choices)
    
        def get_context(self, name, value, attrs):
            context = super().get_context(name, value, attrs)
            context['widget']['attrs']['class']='runner-select'
            return context
    
        def optgroups(self, name, value, attrs=None):
            # Make the initial value available
            self._choices = [(id, runner_to_select_text(Runner.objects.get(pk=id))) for id in value]
            return super().optgroups(name, value, attrs)
    
    
    def runner_select_json(request):
        # You might want to add some permission checking here. Otherwise everybody
        # can get data via this endpoint.
        query = request.GET.get('q', '').strip()
        qs = get_queryset(query)
        items = [dict(value=r.pk, text=runner_to_select_text(r)) for r in qs]
        return JsonResponse(dict(items=items, total_count=len(items)))
    
    def runner_to_select_text(runner):
        return f'{runner.user.first_name} {runner.user.last_name}'
    
    def get_queryset(query):
        if not query:
            return Runner.objects.none()
        return Runner.objects.filter(Q(user__username__icontains=query)|
                                         Q(user__first_name__icontains=query)|
                                         Q(user__last_name__icontains=query)).order_by('user__username')
    
    

    小部件的使用:

    class RaceTeamForm(forms.ModelForm):
        class Meta:
            model = RaceTeam
            widgets = {
                'leader': RunnerSelectWidget(),
            }
    

    JS 在整个页面加载和片段加载时初始化小部件:

    htmx.onLoad(function(elt) {
        var allSelects = htmx.findAll(elt, ".runner-select")
        for( select of allSelects ) {
          new TomSelect(select, {
            // fetch remote data
            load: function(query, callback) {
    
                var url = '/runner_select_json?q=' + encodeURIComponent(query);
                fetch(url)
                    .then(response => response.json())
                    .then(json => {
                        callback(json.items);
                    }).catch(()=>{
                        callback();
                    });
    
            }
        })}});
    

    urls.py

    url('runner_select_json', runner_select_json, name='runner_select_json')
    

    【讨论】:

    • 属性 (set_choices()) 感觉有点脏。如果您知道更简单的解决方案,请告诉我。
    猜你喜欢
    • 1970-01-01
    • 2018-11-08
    • 2023-02-17
    • 1970-01-01
    • 2022-09-29
    • 2012-03-24
    • 2023-01-11
    • 2018-02-17
    • 1970-01-01
    相关资源
    最近更新 更多