neversaint
neversaint

Reputation: 64004

How to make Bootstrap file browse button using django-crispy-forms

Here is my Django forms.py script, using django-crispy-forms

#!/usr/bin/env python
from django import forms
from .models import Method1
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout

class Method1Form(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        """ Use for wrapping bootstrap
        This is crispy stuff.
        """
        super(Method1Form, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_id = 'id-method1Form'
        self.helper.form_class =  'form-horizontal'
        self.helper.label_class = 'col-lg-2'
        self.helper.field_class = 'col-lg-8'
        self.helper.form_method = 'post'
        self.fields['inputfile_param'].label = "Input File"
        self.fields['species_param'].label = "Species"
        self.fields['norm_mode_param'].label = "Normalization"
        self.fields['logscale_param'].label = "Log Scale"
        self.helper.layout = Layout(
                'inputfile_param',
                'species_param',
                'norm_mode_param',
                'logscale_param',
          )
        self.helper.add_input(Submit('submit', 'Submit'))

I can create the following form:

enter image description here

As shown there, I'd like to make the browse button with Bootstrap style. How can achieve that?

I'm thinking of something like this:

enter image description here

Complete HTML rendered by Django looks like this:

/* Stuff for django-crispy */
.asteriskField {
        display: none;
}


.form-control {
    font-size:18px;
    font-family:  "Helvetica Neue",HelveticaNeue;
}

.form-horizontal {
    padding-left: 120px;
    padding-right: 130px;
    font-size:20px;
    font-family:  "Helvetica Neue",HelveticaNeue;
}
<!DOCTYPE html>
<html>
        <head>
       <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
       <meta charset="utf-8">
       </head>


<body>
   
    <!--- DISPLAY THE FORM -->
    

<form  id="id-method1Form" class="form-horizontal" method="post"  enctype="multipart/form-data"> <input type='hidden' name='csrfmiddlewaretoken' value='JdUjVaRwOkOxbQmoeSaSHTaDNTlwjs5U' /> <div id="div_id_inputfile_param" class="form-group"> <label for="id_inputfile_param" class="control-label col-lg-2 requiredField">
                Input File<span class="asteriskField">*</span> </label> <div class="controls col-lg-8"> <input class="clearablefileinput" id="id_inputfile_param" name="inputfile_param" type="file" /> </div> </div> <div id="div_id_species_param" class="form-group"> <label for="id_species_param" class="control-label col-lg-2 requiredField">
                Species<span class="asteriskField">*</span> </label> <div class="controls col-lg-8"> <select class="select form-control" id="id_species_param" name="species_param">
<option value="mouse" selected="selected">Mouse</option>
<option value="human">Human</option>
</select> </div> </div> <div id="div_id_norm_mode_param" class="form-group"> <label for="id_norm_mode_param" class="control-label col-lg-2 requiredField">
                Normalization<span class="asteriskField">*</span> </label> <div class="controls col-lg-8"> <select class="select form-control" id="id_norm_mode_param" name="norm_mode_param">
<option value="genecount_norm" selected="selected">Gene Count</option>
<option value="totalscore_norm">Total Score</option>
</select> </div> </div> <div class="form-group"> <div class="controls col-lg-offset-2 col-lg-8"> <div id="div_id_logscale_param" class="checkbox"> <label for="id_logscale_param" class=""> <input class="checkboxinput" id="id_logscale_param" name="logscale_param" type="checkbox" />
                    Log Scale
                    


    




    



                </label> </div> </div> </div> <div class="form-group"> <div class="aab controls col-lg-2"></div> <div class="controls col-lg-8"> <input type="submit"
    name="submit"
    value="Submit"
    
        class="btn btn-primary"
        id="submit-id-submit"
    
    
    /> </div> </div> </form>

    <!--- END FORM DISPLAY-->

    
</body>


</html>

Upvotes: 5

Views: 4745

Answers (2)

Lindsay Hefton
Lindsay Hefton

Reputation: 41

For those that find this, the previous answer does NOT work. The OP is asking for a file input field, not any type of field with a button on it. It seems like Crispy doesn't plan on ever fixing this issue (see issue). Using FieldWithButtons will create an input field with the same default button in addition to a crispy button that says "Go", like this: Go Browse button

The only crispy-centric way to do this is by creating a Browse button template and simply calling template=<your_template>.html when you make the field. I pulled bits from the BaseInput template to make this and it works.

file_input.html

<label for="{% if input.id %}{{ input.id }}{% else %}{{ input.input_type }}-id-{{ input.name|slugify }}{% endif %}" class="btn btn-secondary">
    {% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
    Browse
    <input type="file"
           name="{% if input.name|wordcount > 1 %}{{ input.name|slugify }}{% else %}{{ input.name }}{% endif %}"
           class="{{ input.field_classes }}"
           id="{% if input.id %}{{ input.id }}{% else %}{{ input.input_type }}-id-{{ input.name|slugify }}{% endif %}"
           hidden
           >
</label>

forms.py

class ProfileUpdateForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ['image']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.layout = Layout(
            # Fieldset('', *self.fields),
            Fieldset('', Button('image', "Browse", css_class="clearablefileinput form-control-file", template="file_input.html", css_id="id_image")),
            FormActions(
                Submit('save', 'Update Profile'),
            )
        )

views.py

def profile(request):
    p_form = ProfileUpdateForm(request.POST, instance=request.user.profile, files=request.FILES)
    #...etc...

Result: Bootstrappy browse button

Upvotes: 2

Corey Gumbs
Corey Gumbs

Reputation: 382

I know this is an old post but for any one interested, Crispy Forms has this in the documentation:

 FieldWithButtons: You can create an input connected with buttons:

 FieldWithButtons('field_name', StrictButton("Go!"))

enter image description here

this is in the docs for crispy forms Bootstrap Layout objects

Upvotes: 1

Related Questions