Conditionally display a Fieldset with Crispy Forms

2019-09-02 02:02发布

问题:

I want to do something simple while using Crispy Forms; I want show a Fieldset only if the user belongs to the staff group. This is easily solved in a standard templates like this:

{% if user.is_staff %} show extra stuff {% endif %}

Maybe I missed something in the manual, but I don't see how I can just inject a template tag like "{% if user.is_staff %}" into the crispy form Layout. It would be ideal for my use case if I could something like the following where I use a fictitious 'Djangotag' to solve my problem:

self.helper.layout = Layout(
   Fieldset(
       'Section One',
       'name',
       'description',
   ),
   Djangotag('{% if user.is_staff %}'),
   Fieldset(
       'Conditional Fieldset',
       'field1',
       'field2',
   ),
   Djangotag('{% endif %}'),      
   Fieldset(
       'More Details',
       'detail1',
       'detail2',
   ),
  )

Is there an easy way to do this with crispy forms?


Note: I already implemented the self.user = kwargs.pop('user') approach and it's not very elegant, I am still looking for something better.

I also tried created simple templates for the if statements, and tried this, HTML("{% include 'helpers/is_staff.html' %}"), but the render process fails.

回答1:

You can pass the request context to your form from the view, and then use this in your form helper. Something like this:

In the view function that creates the form:

form = MyForm(request.POST, user=getattr(request, 'user', None))

Then in your form's __init__ method:

def __init__(self, *args, **kwargs):
    self.user = kwargs.pop('user', None)
    super(MyForm, self).__init__(args, kwargs)

And finally in your form layout code:

if user and user.is_staff:
    self.helper.layout.append(Fieldset(
        'Conditional Fieldset',
        'field1',
        'field2',
    ),

I've just appended this fieldset to the end of the layout. The documentation gives you other options for updating layouts on the fly.



回答2:

I didn't find a cleaner way, so I completed it the way I had started.

Here's the view...

class ModelCreateView(LoginRequiredMixin, CreateView):
....
def get_form_kwargs(self):
    # I am going to stuff the user object into the view so that 
    # I can use it in ModelForm to check the user and build the form
    # conditionally
    kwargs = super(ModelCreateView, self).get_form_kwargs()
    kwargs.update({'user': self.request.user})
    return kwargs

In the ModelForm it was easiest to assume that the fields were always needed -- so I declared them all in the meta section -- then conditionally delete the fields, and conditionally append the form...

class YourCrispyForm(forms.ModelForm):
....
def __init__(self, *args, **kwargs):
    self.user = kwargs.pop('user')
    super(YourCrispyForm, self).__init__(*args, **kwargs)

    # remove conditional fields
    if self.user and self.user.is_staff:
        pass
    else: 
        del self.fields['field_name'] 
        del self.fields['field_name'] 
        del self.fields['field_name']

    if self.user and self.user.is_staff:
        self.helper.layout.append(  
        Fieldset(
            'Conditional Sections',
            Row(
                Div('field_name', css_class="col-md-2"), 
                Div('field_name', css_class="col-md-2"),
                ...
            ),
          )
        )

It took me awhile to realizing that the deletion was the way to go. Hope that helps someone down the line.