Override Django form field's name attr

前端 未结 5 1716
谎友^
谎友^ 2020-12-05 11:23

I\'ve built a Django form that submits to a page on another domain (that I don\'t control). The idea is that I have a nicely styled, neatly generated form that fits neatly i

相关标签:
5条回答
  • 2020-12-05 11:31

    This is a pretty horrible abuse of the API, but there is a form method called add_prefix that is called to determine what the HTML name of each field should be, taking into account the form's prefix if any. You could override that so that it looks up the field name in a dictionary somewhere and returns the name you want - not forgetting to preserve the existing prefix behaviour:

    FIELD_NAME_MAPPING = {
        'field1': 'html_field1',
        'field2': 'html_field2'
    }
    
    class MyForm(forms.ModelForm):
        def add_prefix(self, field_name):
            # look up field name; return original if not found
            field_name = FIELD_NAME_MAPPING.get(field_name, field_name)
            return super(MyForm, self).add_prefix(field_name)
    
    0 讨论(0)
  • 2020-12-05 11:33

    I've implemented a simple function which overwrites the widget render method and assigns a custom name:

    def namedWidget(input_name, widget=forms.CharField):
        if isinstance(widget, type):
            widget = widget()
    
        render = widget.render
    
        widget.render = lambda name, value, attrs=None: \
            render(input_name, value, attrs)
    
        return widget
    

    The usage is simple:

    class AliasCreationForm(forms.Form):
        merchant_id = forms.CharField(
            max_length=30,
            widget=namedWidget('PSPID', forms.HiddenInput),
        )
    
    0 讨论(0)
  • 2020-12-05 11:40

    It's simple. Just use the attribute 'labels' of the class Meta:

    class AuthorForm(ModelForm):
        class Meta:
            model = Author
            fields = ('name', 'title', 'birth_date')
            labels = {
                'name': 'new_name',
            }
    

    where 'new name' will be the name displayed in the html.

    0 讨论(0)
  • 2020-12-05 11:53

    I realize this question is old, but here's another (possibly cleaner) way to do it. Most of the answers here involve monkey patching or overriding a superclass method. This does as well, but I think it's a clean way to do it.

    Create a new Widget that extends the widget you need, such as TextInput:

    class TextInputSansName(forms.TextInput):
        def build_attrs(self, extra_attrs=None, **kwargs):
            if extra_attrs:
                extra_attrs.pop('name', None)
            kwargs.pop('name', None)
            return super().build_attrs(extra_attrs, **kwargs)
    

    Django calls build_attrs on a widget during the render() operation. In the method, I'm removing 'name' if it is in either of the dictionaries. This effectively removes the name just before it renders.

    This answer overrides as little as possible of the Django API.

    In one of my sites, the payment processor needs inputs to be without names. Here's the control declaration in the form class:

    card_number = forms.IntegerField(
        label='Card Number', 
        widget=TextInputSansName(attrs={ 'data-stripe': 'number' })
    ) 
    

    Cheers.

    0 讨论(0)
  • 2020-12-05 11:57

    the name attr is coupled to the name of the property used for the field.

    From your description ("If that other form changes the names of any of its fields, I need to change the names of the fields in my form and then change those names everywhere else in my application - since ", "If the remote form uses silly names then my form object must also have properties with silly names which pollutes my application's code.") you recognize this as a fragile design decision.

    You should consider that your view function totally solves this problem in a trivial way.

    Rather than align names between the remote application and your application, use your view functions to map from your nice names to their horrible names.

    This is what view functions are for.

    To take it a step further, your view function does three things.

    1. Validate inputs. Perhaps persist them in some local database.
    2. Map data from your form to their request structure.
    3. Make the remote request (via httplib or urllib2 or whatever).

    Items 1 and 3 don't change much.

    Item 2 is a field-by-field mapping from the request.POST to a dictionary which you then url lib.urlencode to create the POST request. (Or whatever the protocol is.)

    So break out item 2 into a flexible thing that you specify in your settings.

    settings

    MY_MAPPING_FUNCTION = module.function
    

    In your views.py

    def submit( request ):
       if request.method == POST:
           form = SomeForm( request.POST )
           if is_valid(form):
               form.save() 
               to_be_submitted = settings.MY_MAPPING_FUNCTION( form )
               remote_post( to_be_submitted ) # or whatever your protocol is
    

    Add the mapping module to your application

    module.py

    def version_1_2( form ):
        return { 
            'silly_name_1': form.cleaned_data['your_nice_name'], 
            'from': form.cleaned_data['another_nice_name'],
        }
    
    def version_2_1( form ):
        return {
             'much_worse_name': form.cleaned_data['your_nice_name'], 
             'from': form.cleaned_data['another_nice_name'],
        }
    
    0 讨论(0)
提交回复
热议问题