Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change the widget of just one field in django-crispy-forms with the layout object AppendedText

I am using django-crispy-forms to render the form of a model in django. django-crispy-forms already integrates very well with Twitter Bootstrap and has defined a layout object for the AppendedText widget:

AppendedText widget

I have the following model in my django models.py

class Historia(models.Model):
    persona         = models.ForeignKey('Persona', 
                                  blank=False, 
                                  null=True,
                                  related_name='historias')
    fecha           = models.DateTimeField(unique=True, 
                                auto_now_add=True)
    motivo          = models.TextField()
    peso            = models.DecimalField(blank=True,
                                   max_digits=5, decimal_places=2)
    talla           = models.IntegerField(blank=True)
    tension         = models.CharField(max_length=15)
    pulso           = models.IntegerField()
    diagnostico     = models.TextField()
    tratamiento     = models.TextField()
    pendiente       = models.TextField(blank=True)

and I define the following modelform in forms.py:

class HistoriaForm(django.forms.ModelForm):
    class Meta:
        model = models.Historia

    def __init__(self, *args, **kwargs):
        super(HistoriaForm, self).__init__(*args, **kwargs)

        self.helper = FormHelper(self)
        self.helper.form_class = 'form-horizontal'
        self.helper.form_tag = True
        self.helper.form_id = 'nueva_historia_form'
        self.helper.form_action = 'nueva_historia'

        self.helper.form_show_errors = True
        self.helper.error_text_inline = False

        self.helper.add_input(Submit('crear', 'Crear'))

My question is how can I change the widget of the peso field without having to create a new widget subclass since django-crispy-forms already defines a layout object. Please also note that I do not want to enumerate all other fields in order to do this like:

self.helper.layout = Layout(
            Fieldset(
                'Legend',
                'nombre',
                'apellido',
                AppendedText('peso', 'kg')
            ),
            ButtonHolder(
                Submit('submit', 'Submit', css_class='button white')
            )
        )

since I will have to keep editing the helper layout if I decide to add new fields in my model from which standard widgets are already very useful.

Edit: I have also seen the methods to access the layouts in a helper like self.helper.layout[0] = AppendedText('peso', 'kg') but I do not know the index of the field I would like to change and it would be much nicer if I could just write self.helper.layout['peso'] = AppendedText('peso', 'kg')

like image 348
Gustavo Torres Avatar asked Feb 18 '23 03:02

Gustavo Torres


2 Answers

django-crispy-forms does have an API for these kind of things: http://django-crispy-forms.readthedocs.org/en/latest/dynamic_layouts.html

You could simply do:

form.helper['peso'].wrap(AppendedText, "kg")
like image 91
maraujop Avatar answered Feb 19 '23 18:02

maraujop


Ok, this is what worked for me without having to be too verbose and keeping it simple. In the __init__ method just do the following:

for i, field in enumerate(self.helper.layout):
    if field == 'peso':
        self.helper.layout[i] = AppendedText('peso', 'kg')

This works perfectly fine if the field you are looking for is in the first level of hierarchy in the layout object, otherwise you would have to be more specific about what you are looking. For example if your field is in a DIV container you would first will have to check whether field.__class__ == DIV and then introspect into the DIV field which is not as easy as it seems.

It would be nice, however, to have the API of the layout object to support filter queries

like image 43
Gustavo Torres Avatar answered Feb 19 '23 16:02

Gustavo Torres