I find myself in a conundrum with related models and related formsets. I'll try and simplify a rather complex set of relations to illustrate the bare bones gist of my issue. Imagine the standard Djnago docs example:
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
Now imagine I have a form in which I can create (or update, same issues arise) a Musician and their Albums. I have a generic CreateView of Musician and on it a formset of Albums. You can enter a Musician's name and data and their albums and submit and it creates the objects in the database. All this I have working fine. Can even us JS to dynamically alter the formset and allow submission of any number of albums, and updated (with an UpdateView) of such record families.
Further, note that in Musician a field is created, "album_set" that is the set of Albums associated with that Musician.
Now I can put a clean() method in Musician, and it's works really charmingly. If I raise a ValidattionError in clean() the form re-renders with the message displayed (it's available in the context item "messages"). I love this it's very slick and easy.
Now let's say I have a criterion to enforce, say "A musician must have at least one album."
I'd like to test for that and use this neat feedback mechanism to tell the user. Alas when I get to clean() in Musician, the field album_set is None as the Albums have not yet been cleaned and saved.
In my CreateView I can override is_valid() to do some checks as well, but I have the same problem, none of the objects exist yet so I can't check of integrity of the objects,.
I can override form_valid() and in that I save the submitted Musician and Albums explicitly (this all works a dream) and after that can check for integrity (that the Musician has at least 1 album), but I can't at this point raise a ValidationError anymore to bounce back to the form with a message. Validation is done with in is_valid and in form_valid() is a little late to be doing it.
The Catch 22 I see is that to validate the relationships I need the objects to exist and to save the objects I want the relationships validated.
Ouch. A conundrum.
I can imagine saving them all in a transaction and bailing if something is awry (rolling back), all not a conceptual issue, but again in form_valid() is a tad late, and in is_valid() - which calls clean() - the objects don't exist yet.
Conclusion: I have to validate the request not the objects. But that is not DRY. The code that parses a request and turns it into objects is inside of Django. I don't want to repeat the effort. I want to hook into it at the appropriate place to:
1) Validate the relationships between submitted models (in one HTML form, multiple Django forms)
2) Do it at a time that allows easy fallback on the lovely message system.
And all I've concluded is form_valid() seems too late, is_valid() seems too early but I see nothing between them. Is there place to hook into the post processing that is most appropriate for this kind of need?
Please don't dwell on the specifics of the example, I actually have richer more complex relationships with more formsets than that all working fine I can create, update, list and work with it. All charming. I can validate intra-model fields in clean() easily and that works a dream. But I'm stuck on how to validate relationships, when and where that is best done.
Regards,
Bernd.