# django custom form - removing fields based on user

**URL:** <https://forum.djangoproject.com/t/django-custom-form-removing-fields-based-on-user/4291>\
**Category:** Using Django\
**Created:** [September 10, 2020, 11:01pm UTC](https://forum.djangoproject.com/t/django-custom-form-removing-fields-based-on-user/4291 "2020-09-10T23:01:27Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![anon36149362](https://avatars.discourse-cdn.com/v4/letter/a/bc8723/32.png) [@anon36149362](https://forum.djangoproject.com/u/anon36149362)\
**Post date:** [September 10, 2020, 11:01pm UTC](https://forum.djangoproject.com/t/django-custom-form-removing-fields-based-on-user/4291/1 "2020-09-10T23:01:27Z")

</div>

When logged on as a superuser, I want the edit form to have access to ‘span’ field.

What am I doing wrong here?

```auto
class ArticleEditForm(forms.ModelForm):
    
    class Meta:
        model = Article
        fields = ('title','category','intro','content','image',)
        
         
    def __init__ (self, *args, **kwargs):
            
            user = kwargs.pop('user', None)

            if user.is_superuser:
               self.Meta.fields['span'] = forms.CharField()
           
            super(). __init__ (*args, **kwargs)

```

I realise this is because you can’t change the tuple. How do I do this?

I managed previously to do this on the model, before I installed ckeditor, and now since I use the custom form, I can’t get it working.

I’ve also tried to change self.Meta in the constructor, but that didn’t seem to do anything.

```auto
self.Meta.fields = ('title','category','intro','content','image', 'span',)

```

my very basic knowledge of python inner classes, and Django’s class hierarchy is letting me down.

my view code

```auto
class Article_Update(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
    template_name = "articles/article_update.html"
    form_class = ArticleEditForm
    model = Article
    #fields = ('title','category','intro','content','image',)
    def test_func(self):
        article = self.get_object()
        if self.request.user == article.author or self.request.is_superuser :
           
           #self.fields = ('title','category','intro','content','image','span') 
           return True
        return False

    # Sending user object to the form, to verify which fields to display/remove (depending on group)
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs.update({'user': self.request.user})
        return kwargs

```

---

<div class="post-metadata">

**Author:** ![datalowe](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/datalowe/32/2360_2.png) [@datalowe](https://forum.djangoproject.com/u/datalowe)\
**Post date:** [September 11, 2020, 1:44am UTC](https://forum.djangoproject.com/t/django-custom-form-removing-fields-based-on-user/4291/2 "2020-09-11T01:44:39Z")

</div>

It might help to consider what is what. I’ll try to explain things as I understand them, but anyone is very welcome to correct me where I’m wrong.

## Meta class

When you define a class (e. g. `Meta`) inside of a class’ (e. g. `ArticleEditForm`) definition, the inner class is bound to the outer class itself. It’s similar to class attributes in the sense that upon generating instances, there is no new “copy” of the inner class being formed. So for example, if you do this…

```auto
f1 = MyForm()
f2 = MyForm()

```

… then `f1.Meta` and `f2.Meta` both refer to the same thing/class. This becomes clear if you do the following:

```auto
f1.Meta is f2.Meta # True
f1.Meta is MyForm.Meta

```

the point of the `Meta` class is to describe “meta characteristics” of the outer class (Form) itself and, by extension, **all** instances of the outer class (forms). The Meta class cannot be used for changing the behavior of individual instances. So for example:

```auto
f1.Meta.fields = ['bar', 'baz']

```

since `f1.Meta` refers to the same class as e. g. `f2.Meta` or `MyForm.Meta`, doing something like this affects the behavior/meta characteristics of `MyForm` itself and **all** current or future instances (forms).

## Meta.fields

The `Meta.fields` attribute isn’t itself a collection of fields. It’s just meta data, saying which of the model’s (Article) fields that Form instances’ field collections (dictionaries, with field names as keys and Field instances as values) should be based on. It’s like part of a blueprint, a specification, for how individual forms’ collections of fields should be constructed. So trying to add an actual field (e. g. a `forms.CharField` instance) to this specification doesn’t make sense. This also follows from what `Meta` actually is, as described above. Speaking of which, based on what I’ve written so far you might want to ponder why it makes sense to force `Meta.fields` to be immutable (i. e. a tuple).

## form\_instance.fields

What you actually want is to include an additional field (a `forms.CharField` instance) for certain `ArticleEditForm` instances. If we look in Django’s [code base](https://github.com/django/django/blob/master/django/forms/forms.py), where the `BaseForm` class is defined, specifically its ` __init__ ` method, we can read this (around line 82):

```auto
        # The base_fields class attribute is the *class-wide* definition of
        # fields. Because a particular *instance* of the class might want to
        # alter self.fields, we create self.fields here by copying base_fields.
        # Instances should always modify self.fields; they should not modify
        # self.base_fields.
        self.fields = copy.deepcopy(self.base_fields)

```

This is more or less the answer to your question. You should be looking at modifying `ArticleEditForm` instances by modifying their `.fields` attribute.

Hopefully you can now see why a possible solution, or at least something that’s closer to what you want, might be:

```auto
class ArticleEditForm(forms.ModelForm):
    """
    [descriptive docstring...]
    """
    class Meta:
        model = Article
        fields = ('title','category','intro','content','image',)

    def __init__ (self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(). __init__ (*args, **kwargs)
        
        if user.is_superuser:
            self.fields['span'] = forms.CharField()

```

An alternative, possibly slightly more efficient (someone more knowledgeable please chime in on this) solution might be to create a `ArticleSuperEditForm` class that inherits from ArticleEditForm and has a modified Meta class, and then you put logic for deciding which kind of form to use in your views. I don’t know if this difference is large enough to be meaningful to the point where you would consider going with this less concise and less “fat model”-based solution, but I’m guessing no.

If you want to properly learn about OOP in Python, you might like this [tutorial series by coreyms](https://www.youtube.com/watch?v=ZDa-Z5JzLYM&list=PL-osiE80TeTsqhIuOqKhwlXsIBIdSeYtc). I’ve only watched a couple of videos of it (having learned OOP from various other sources), but I’ve done the same author’s series on Django and it was very good.

---

<div class="post-metadata">

**Author:** ![anon36149362](https://avatars.discourse-cdn.com/v4/letter/a/bc8723/32.png) [@anon36149362](https://forum.djangoproject.com/u/anon36149362)\
**Post date:** [September 11, 2020, 8:35pm UTC](https://forum.djangoproject.com/t/django-custom-form-removing-fields-based-on-user/4291/3 "2020-09-11T20:35:52Z")

</div>

Hi there, thanks for the reply. I did finally get this working.

```auto
class Article_Update(LoginRequiredMixin,UserPassesTestMixin,UpdateView):

    template_name = "articles/article_update.html"
   # form_class = ArticleEditForm
    model = Article
   # fields = ('title','category','intro','content','image',)
    def test_func(self):
        article = self.get_object()
         
        if self.request.user == article.author or self.request.user.is_superuser :
           return True
        return False
    
    def dispatch(self, request, *args, **kwargs):
        # Check permissions for the request.user here
        article = self.get_object()
        self.fields = ['title','category','intro','content','image',]
        if request.user.is_superuser :
            self.fields += ['span'] #denoting number of columns taken up
        
        return super().dispatch(request, *args, **kwargs)

```

I have no idea if this is the ‘gold-standard’ way to do it, but it works. I basically saw something on Stack exchange which led me to the solution.

As you can see, I took off the form class, because I realised I didn’t need to create my own. face palm. But what you’ve given me will help when I do.

I really do need get a better understanding of inner classes.
