2011-06-25 41 views
9

我必须将标题属性添加到ModelChoiceField的选项中。这是我为管理代码:如何在django的选项标签中添加属性?

class LocModelForm(forms.ModelForm): 
     def __init__(self,*args,**kwargs): 
      super(LocModelForm,self).__init__(*args,**kwargs) 
      self.fields['icons'] = forms.ModelChoiceField(queryset = Photo.objects.filter(galleries__title_slug = "markers")) 
      self.fields['icons'].widget.attrs['class'] = 'mydds' 


     class Meta: 
      model = Loc 
      widgets = { 
       'icons' : forms.Select(attrs={'id':'mydds'}), 
       } 

     class Media: 
      css = { 
       "all":("/media/css/dd.css",) 
       } 
      js=(
       '/media/js/dd.js', 
       ) 

class LocAdmin(admin.ModelAdmin): 
    form = LocModelForm 

我可以添加任何属性来选择窗口小部件,但我不知道如何添加属性选项标签。任何想法 ?

回答

16

首先,不修改__init__领域,如果你想覆盖部件使用Meta内部类,如果要覆盖表单域,声明它们像一个正常的(非模式)的形式。

如果Select小部件没有做到你想要的,那么只需制作你自己的。原始小部件使用render_option方法获取单个选项的HTML表示 - 创建子类,覆盖它并添加所需的任何内容。

class MySelect(forms.Select): 
    def render_option(self, selected_choices, option_value, option_label): 
     # look at the original for something to start with 
     return u'<option whatever>...</option>' 

class LocModelForm(forms.ModelForm): 
    icons = forms.ModelChoiceField(
     queryset = Photo.objects.filter(galleries__title_slug = "markers"), 
     widget = MySelect(attrs = {'id': 'mydds'}) 
    ) 

    class Meta: 
     # ... 
     # note that if you override the entire field, you don't have to override 
     # the widget here 
    class Media: 
     # ... 
+3

有什么特别的原因,为什么字段应该在'Meta'内部类而不是'__init__'方法中修改?并且同样的推理适用于修改/添加字段的小部件属性? – hellsgate

+1

@hellsgate在大多数情况下,没有理由不重写'__init__'来设置'widget.attrs'。在大多数用例中,例如修改默认小部件上的html属性*而不是通过'__init__'覆盖来覆盖DRY。不幸的是,对于OP的情况,他将不得不定义一个自定义小部件,因为'

+0

这种方法也可以用来扩展'SelectMultiple'小部件。只是子类'SelectMultiple'并将其传递给自定义的'MySelect'小部件。 – NickBraunagel

4

下面是从forms.Select类我做了继承(感谢Cat Plus Plus你为我开始使用此)。初始化时,请提供option_title_field参数,指示将哪个字段用于<option>标题属性。

from django import forms 
from django.utils.html import escape 

class SelectWithTitle(forms.Select): 
    def __init__(self, attrs=None, choices=(), option_title_field=''): 
     self.option_title_field = option_title_field 
     super(SelectWithTitle, self).__init__(attrs, choices) 

    def render_option(self, selected_choices, option_value, option_label, option_title=''): 
     print option_title 
     option_value = forms.util.force_unicode(option_value) 
     if option_value in selected_choices: 
      selected_html = u' selected="selected"' 
      if not self.allow_multiple_selected: 
       # Only allow for a single selection. 
       selected_choices.remove(option_value) 
     else: 
      selected_html = '' 
     return u'<option title="%s" value="%s"%s>%s</option>' % (
      escape(option_title), escape(option_value), selected_html, 
      forms.util.conditional_escape(forms.util.force_unicode(option_label))) 

    def render_options(self, choices, selected_choices): 
      # Normalize to strings. 
      selected_choices = set(forms.util.force_unicode(v) for v in selected_choices) 
      choices = [(c[0], c[1], '') for c in choices] 
      more_choices = [(c[0], c[1]) for c in self.choices] 
      try: 
       option_title_list = [val_list[0] for val_list in self.choices.queryset.values_list(self.option_title_field)] 
       if len(more_choices) > len(option_title_list): 
        option_title_list = [''] + option_title_list # pad for empty label field 
       more_choices = [(c[0], c[1], option_title_list[more_choices.index(c)]) for c in more_choices] 
      except: 
       more_choices = [(c[0], c[1], '') for c in more_choices] # couldn't get title values 
      output = [] 
      for option_value, option_label, option_title in chain(more_choices, choices): 
       if isinstance(option_label, (list, tuple)): 
        output.append(u'<optgroup label="%s">' % escape(forms.util.force_unicode(option_value))) 
        for option in option_label: 
         output.append(self.render_option(selected_choices, *option, **dict(option_title=option_title))) 
        output.append(u'</optgroup>') 
       else: # option_label is just a string 
        output.append(self.render_option(selected_choices, option_value, option_label, option_title)) 
      return u'\n'.join(output) 

class LocModelForm(forms.ModelForm): 
    icons = forms.ModelChoiceField(
     queryset = Photo.objects.filter(galleries__title_slug = "markers"), 
     widget = SelectWithTitle(option_title_field='FIELD_NAME_HERE') 
    ) 
0

我有一个类似的问题,我需要为每个选项动态添加一个自定义属性。但在Django 2.0中,html渲染已移入Widget基类,因此修改render_option不再有效。这里是为我工作的解决方案:

from django import forms 

class CustomSelect(forms.Select): 
    def __init__(self, *args, **kwargs): 
     self.src = kwargs.pop('src', {}) 
     super().__init__(*args, **kwargs) 

    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): 
     options = super(CustomSelect, self).create_option(name, value, label, selected, index, subindex=None, attrs=None) 
     for k, v in self.src.items(): 
      options['attrs'][k] = v[options['value']] 
     return options 

class CustomForm(forms.Form): 
    def __init__(self, *args, **kwargs): 
     src = kwargs.pop('src', {}) 
     choices = kwargs.pop('choices',()) 
     super().__init__(*args, **kwargs) 
     if choices: 
      self.fields['custom_field'].widget = CustomSelect(attrs={'class': 'some-class'}, src=src, choices=choices) 

    custom_field = forms.CharField(max_length=100) 

然后在视图中,渲染{'form': CustomForm(choices=choices, src=src)}上下文,其中src是这样一个字典:{'attr-name': {'option_value': 'attr_value'}}

相关问题