19 Stimmen

Wie fügt man Attribute zu Optionstags hinzu?

Ich muss das Attribut title zu den Optionen des ModelChoiceFields hinzufügen. Hier ist mein Admin-Code für das:

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

Ich kann dem Select-Widget jedes beliebige Attribut hinzufügen, aber ich weiß nicht, wie man Attribute zu Optionstags hinzufügt. Irgendeine Idee?

20voto

Cat Plus Plus Punkte 119072

Zunächst einmal sollten Sie keine Felder in __init__ Wenn Sie Widgets außer Kraft setzen wollen, verwenden Sie Meta wenn Sie Formularfelder überschreiben wollen, deklarieren Sie sie wie in einem normalen (nicht-modellbasierten) Formular.

Wenn die Select Widget nicht das tut, was Sie wollen, dann machen Sie einfach Ihr eigenes. Das Original-Widget verwendet render_option Methode, um die HTML-Darstellung für eine einzelne Option zu erhalten - erstellen Sie eine Unterklasse, überschreiben Sie sie und fügen Sie hinzu, was Sie wollen.

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:
        # ...

7voto

kayoz Punkte 1044

Ich hatte ein ähnliches Problem, bei dem ich ein benutzerdefiniertes Attribut dynamisch zu jeder Option hinzufügen musste. Aber in Django 2.0 wurde das HTML-Rendering in die Widget-Basisklasse verschoben, so dass das Ändern render_option funktioniert nicht mehr. Hier ist die Lösung, die bei mir funktioniert hat:

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)

Rendern Sie dann in Ansichten einen Kontext mit {'form': CustomForm(choices=choices, src=src)} donde src ist ein Wörterbuch wie dieses: {'attr-name': {'option_value': 'attr_value'}} .

5voto

p14z Punkte 1642

Hier ist eine Lösung, wenn Sie die Instanz verwenden möchten, um den Attributwert zu setzen.

class IconSelectWidget(forms.Select):
    def create_option(self, name, value, *args, **kwargs):
        option = super().create_option(name, value, *args, **kwargs)
        if value:
            icon = self.choices.queryset.get(pk=value)  # get icon instance
            option['attrs']['title'] = icon.title  # set option attribute
        return option

class LocModelForm(forms.ModelForm):
    icons = forms.ModelChoiceField(
        queryset=Photo.objects.filter(galleries__title_slug='markers'),
        widget=IconSelectWidget
    )

4voto

Joe Tricarico Punkte 353

Hier ist eine Klasse, die ich erstellt habe und die von forms.Select erbt (Dank an Katze Plus Plus dafür, dass ich damit angefangen habe). Geben Sie bei der Initialisierung die option_title_field Parameter, der angibt, welches Feld für die <option> Titel-Attribut.

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')
    )

2voto

sbutler Punkte 608

Bei der Arbeit mit Django 1.11 habe ich eine andere Möglichkeit entdeckt, dies mit den dokumentierten APIs zu erreichen. Wenn Sie überschreiben get_context und tief genug in die Struktur eindringen, werden Sie die einzelnen Optionsattribute in context['widget']['optgroups'][1][option_idx]['attrs'] . In meiner Unterklasse habe ich zum Beispiel diesen Code:

class SelectWithData(widgets.Select):
    option_data = {}

    def __init__(self, attrs=None, choices=(), option_data={}):
        super(SelectWithData, self).__init__(attrs, choices)
        self.option_data = option_data

    def get_context(self, name, value, attrs):
        context = super(SelectWithData, self).get_context(name, value, attrs)
        for optgroup in context['widget'].get('optgroups', []):
            for option in optgroup[1]:
                for k, v in six.iteritems(self.option_data.get(option['value'], {})):
                    option['attrs']['data-' + escape(k)] = escape(v)
        return context

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X