2011-06-25 10 views
9

Muszę dodać atrybut title do opcji modelu ModelChoiceField. Oto mój kod administratora:Jak dodać atrybuty do znaczników opcji w 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 

Mogę dodać dowolny atrybut do wyboru widżetu, ale nie wiem, jak dodać atrybuty do znaczników opcji. Dowolny pomysł ?

Odpowiedz

16

Po pierwsze, nie modyfikuj pól w __init__, jeśli chcesz zastąpić widgety, użyj klasy wewnętrznej Meta, jeśli chcesz zastąpić pola formularza, deklaruj je jak w normalnej (nie-modelowej) formie.

Jeśli widget Select nie robi, co chcesz, po prostu stwórz własny. Oryginalny widget używa metody render_option, aby uzyskać reprezentację HTML dla pojedynczej opcji - utworzyć podklasę, zastąpić ją i dodać, co chcesz.

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

Czy istnieje jakiś szczególny powód, dla którego pola powinny być modyfikowane w wewnętrznej metodzie "Meta" zamiast w metodzie "__init__"? Czy to samo rozumowanie miałoby zastosowanie do modyfikowania/dodawania atrybutów widżetów dla pola? – hellsgate

+1

@hellsgate W większości przypadków nie ma powodu, aby nie przesłonić '__init__', aby ustawić' widget.attrs'. W większości przypadków użycia, takich jak modyfikowanie atrybutów html na innym domyślnym widżecie * nie * robienie tego za pośrednictwem '__init__' narusza DRY. Niestety dla przypadku OP, będzie musiał zdefiniować niestandardowy widget, ponieważ znacznik '

+0

Podejście to można również wykorzystać do rozszerzenia widżetu 'SelectMultiple'. Po prostu podklasuj 'SelectMultiple' i przekaż mu niestandardowy widżet' MySelect'. – NickBraunagel

4

Oto klasa zrobiłem, która dziedziczy z forms.Select (dzięki Cat Plus Plus za wprowadzenie mnie zaczęło się to). Podczas inicjowania podaj parametr option_title_field wskazujący pole, którego chcesz użyć dla atrybutu tytułu <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

Miałem podobny problem, gdy potrzebowałem dynamicznie dodawać niestandardowy atrybut do każdej opcji. Ale w Django 2.0 rendering html został przeniesiony do klasy bazowej Widget, więc modyfikowanie render_option już nie działa. Oto rozwiązanie, które pracował dla mnie:

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) 

Następnie w widokach, kontekst renderowania z {'form': CustomForm(choices=choices, src=src)} gdzie src jest słownikiem tak: {'attr-name': {'option_value': 'attr_value'}}.

Powiązane problemy