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.
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…
f1 = MyForm()
f2 = MyForm()
f2.Meta both refer to the same thing/class. This becomes clear if you do the following:
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:
f1.Meta.fields = ['bar', 'baz']
f1.Meta refers to the same class as e. g.
MyForm.Meta, doing something like this affects the behavior/meta characteristics of
MyForm itself and all current or future instances (forms).
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).
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, where the
BaseForm class is defined, specifically its
__init__ method, we can read this (around line 82):
# 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.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
Hopefully you can now see why a possible solution, or at least something that’s closer to what you want, might be:
model = Article
fields = ('title','category','intro','content','image',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
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. 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.