2

I have a model named Foo. I can create a new instance of this model, and save it to the database, even if I assign a field name that doesn't exist. For instance:

foobar = Foo.objects.create()
foobar.abcdefg = True # The field abcdefg does not exist!
foobar.full_clean()
foobar.save()

Why won't Django throw an exception when assigning to a field that doesn't exist? How can I make it throw an exception, at least during full_clean?

Flimm
  • 136,138
  • 45
  • 251
  • 267

2 Answers2

1

foobar is just an instance of a class. As with any instance, you can add attributes whenever you want:

>>> class Test(object):
...     myval = 7
... 
>>> t = Test()
>>> t.myval
7
>>> t.otherval = 'another'
>>> t.otherval
'another'

(As noted by Flimm in a comment, you can prevent this by adding a __slots__ attribute to your class)

You could use self.__dict__ in the full_clean method to check for extraneous attributes, but you will need to be able to determine what attributes are supposed to be there. Note that model instances have a _state attribute that will need to be left alone.

You can get a list of the field names on your model using this list comprehension (From this answer):

[f.name for f in self._meta.get_fields()]

So, then just iterate over self.__dict__ and raise an exception if any keys are found that are not in that list of model fields and do not start with an underscore.

FamousJameous
  • 1,565
  • 11
  • 25
  • [Using `__slots__`](http://book.pythontips.com/en/latest/__slots__magic.html), you could actually make it impossible to add new attributes whenever you want. – Flimm Jun 13 '17 at 13:09
  • That's a good point. However, you would need to check what attributes Django adds to your model and include those in your `__slots__` to make sure it still works. – FamousJameous Jun 13 '17 at 14:30
  • seems you could add `"__dict__"` to the `__slots__` list of variable strings. Not sure how much good it would do (if it affects memory poorly), but via the docs at https://docs.python.org/3.6/reference/datamodel.html#notes-on-using-slots you can. – jheld Feb 12 '19 at 21:50
1

Python lets you store attributes of any name on virtually on any instance. It's possible to block this (either by writing the class in C).

The reason it works is that most instances store their attributes in a dictionary. The dictionary is stored in an instance attribute called __dict__. In fact, some people say "classes are just syntactic sugar for dictionaries." That is, you can do everything you can do with a class with a dictionary; classes just make it easier.

You're used to static languages where you must define all attributes at compile time. In Python, class definitions are executed, not compiled; classes are objects just like any other; and adding attributes is as easy as adding an item to a dictionary. This is by design.

Actually the mere fact is that there is no such thing as a declaration. That is, you never declare "this class has a method foo" or "instances of this class have an attribute bar", let alone making a statement about the types of objects to be stored there. You simply define a method, attribute, class, etc. and it's added.

zaidfazil
  • 9,017
  • 2
  • 24
  • 47
  • Again, [using `__slots__`](http://book.pythontips.com/en/latest/__slots__magic.html) is a counter-example to this answer, objects don't always use `__dict__`. – Flimm Jun 13 '17 at 13:10