Where does Model.save() make a list of fields?

I intend to override CreateView and UpdateView to set some audit fields

class CreateView(CreateView):

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        # I set below through middleware
        project_object = self.request.current_project
        form.instance.project = project_object
        return super().form_valid(form)

I’m trying to find out what happens if I set form.instance.foo = bar when foo isn’t a field defined on the model.

So I decided to look at how Model.save() works. I noticed that Django has a guard against non-defined fields in case of update_fields was set. non-model fields check.

So I wanted to double check if there is something like that further down the line. I followed Model.save() to Model._save_table(). The fields seem to be collected in non_pk.

I then followed meta.local_concrete_fields and reached Options.local_fields.

I wasn’t able to see where local_fields is set.

Update
Looks like in Modelbase.__ new__() attributes that have a contribute_to_class method (fields) are collected in contributabe_attrs. Then these attributes are passed to add_to_class which calls their contribute_to_class method. Finally, add_field() is called, and this populates local_fields

Conclusion
If we set form.instance.foo = bar given that foo wasn’t defined in the model, foo will be ignored and not affect the saving process. The exception to this is when we specify update_fields, there is a check in-place in this case.

Yup, that sounds abour right. There’s a whole load of stuff in the model’s _meta which is what gets iterated through for “fields” - since Python doesn’t distinguish between field values on a model and other things like e.g. methods or cache markers, that’s how Django knows what to ignore.