2011-06-25 30 views
9

Tengo que agregar el atributo title a las opciones de ModelChoiceField. Aquí está mi clave de administrador para que:¿Cómo agregar atributos a las etiquetas de opción en 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 

puedo añadir cualquier atributo para seleccionar widgets, pero no sé cómo agregar atributos a etiquetas de opción. Alguna idea ?

Respuesta

16

En primer lugar, no modifican los campos en __init__, si desea anular widgets utilizan Meta clase interna, si desea anular los campos del formulario, declararlos como en una forma normal (no-modelo).

Si el widget Select no hace lo que usted desea, simplemente haga el suyo. El widget original usa el método render_option para obtener una representación HTML para una sola opción: crear una subclase, anularla y agregar lo que desee.

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

¿Hay alguna razón en particular por la cual los campos se deben modificar en la clase interna 'Meta' en lugar del método' __init__'? ¿Y se aplicaría el mismo razonamiento para modificar/agregar atributos de widgets para un campo? – hellsgate

+1

@hellsgate En la mayoría de los casos, no hay razón para no anular '__init__' para establecer' widget.attrs'. En la mayoría de los casos de uso, como la modificación de los atributos html en un widget predeterminado * no * que lo hace a través de '__init__', la anulación viola DRY. Desafortunadamente para el caso del OP, tendrá que definir un widget personalizado ya que la etiqueta '

+0

Este enfoque también se puede usar para extender el widget 'SelectMultiple'. Simplemente haga una subclase 'SelectMultiple' y pásele el widget personalizado' MySelect'. – NickBraunagel

4

Aquí está una clase que hice que hereda de forms.Select (gracias a Cat Plus Plus por iniciarme con esto). Al inicializar, proporcione el parámetro option_title_field que indica qué campo usar para el atributo de título <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

tuve un problema similar, donde tenía que añadir un atributo personalizado a cada opción de forma dinámica. Pero en Django 2.0, la representación html se movió a la clase base de widgets, por lo que modificar render_option ya no funciona. Aquí está la solución que funcionó para mí:

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) 

Luego, en vistas, rinden un contexto con {'form': CustomForm(choices=choices, src=src)} donde src es un diccionario de esta manera: {'attr-name': {'option_value': 'attr_value'}}.

Cuestiones relacionadas