It saves the form correctly, how ever when it renders again appears the id value instead the name value. How may I change the display values of a django form? I think overriding init will do it, but can't find where is the value property

2 Answers
2

I just wrote Field and Widget subclasses, that solve this particular problem and could be used with JS autocompletion, for example - and is reusable. Still, it required more work than your solution and I'm not sure whether you'll want to use mine or not. Either way - I hope I'll get few upvotes - I spent quite some time and effort writing this...

Instead of defining your ModelForm like you did and messing with clean_ I suggest something like that:

Now, CustomModelChoiceField (I can't think of better name for the class) is ModelChoiceField subclass, which is good, because we can use queryset argument to narrow acceptable choices. If widget argument is not present, like above, the default one for this field is used (more about it later). query_field is optional and defaults to "pk". So, here is the field code:

What body of __init__ means is that setting widget = None during creation of CustomModelChoiceField gives us plain ModelChoiceField (which was very helpful while debugging...). Now, actual work is done in ModelTextInput widget:

It's essentially TextInput, that is aware of two additional things - which attribute of which model it represents. ( model_class should be replaced with queryset for narrowing of possible choices to actually work, I'll fix it later). Looking at implementation of value_from_datadict it's easy to spot why to_python in the field had to be overridden - it expects int value, but does not check if it's true - and just passes the value to associated model, which fails with ugly exception.

I tested this for a while and it works - you can specify different model fields by which form field will try to find your artist, form error handling is done automatically by base classes and you don't need to write custom clean_ method every time you want to use similar functionality.