views:

57

answers:

2

Hey everyone!

I'm a little stumped here, I can't find what I'm looking for in the Django docs...

What I want, is to be able to override the Save method of an Model. I want it to check for a certain set of conditions - if these conditions are met, it will create the object just fine, but if the conditions are not met, I want to raise an error. The main thing is that I am using the Admin interface for most of these, so I this isn't an error that I will catch myself - this is an error that I need the admin interface to catch and display to the user.

How might I go about doing this chaps? Is there documentation I am missing out on reading? Oh, also to note, I am using Django 1.1, and thus, cannot override the clean / full_clean methods introduced by Django 1.2.

Thanks! Shawn

+1  A: 

I want ... to check for a certain set of conditions - if these conditions are met, it will create the object just fine, but if the conditions are not met, I want to raise an error.

This is what a ModelForm is for.

http://docs.djangoproject.com/en/dev/topics/forms/modelforms/

S.Lott
That is my one pet peeve about Django (prior to 1.2)... the business rules surrounding a Model belongs in the Model, not a ModelForm - a ModelForm is only a single representation of a single way that a Model could be created/modified. A web service can't readily re-use any of these business rules that are all jammed into the presentation layer instead of the model layer where they belong. Of course, there are valid scenarios for logic to be in model forms... but business rules belong in Models.
Matthew J Morrison
@Matthew J Morrison: Don't agree at all. "Validation" is only one of many kinds of business rules. We have separate forms for HTML pages, RESTful web services and batch file uploads. Different ways of validating the input against a common model. We have business rules in the Model. Business rules in the Views. And Business rules in separate modules.
S.Lott
I'm referring to business rules that always hold true for a given model. "If A is provided for model X then B is required and C should be one of these valid choices". Putting this in a ModelForm will require you to put this exact code in every different ModelForm, every different RESTful web service, and in every batch file upload process when it should be in the Model because it is always true regardless of how you are interacting with that Model.
Matthew J Morrison
"every different ModelForm"? I think that good-old inheritance saves repetition here. Also, it can be refactored into a distinct class or function with no problem at all. I'm not clear on why you're claiming we must copy-and-paste the business rules. We have them exactly once. And use them as necessary in Model, Form, View, etc.
S.Lott
sure you can come up with ways to not actually have the code located in multiple places, but that is not necessarily what I'm getting at. What I'm trying to say is that if you have logic that GOVERNS data, then that logic needs to be as close as it can possibly be to that data so that it can absolutely never be bypassed. My concern isn't so much about duplication, in this case, more about making sure the data quality is consistent, which cannot be done if the code that governs that data is different depending on how you get to that data.
Matthew J Morrison
not to get even further off topic but, "good-old inheritance saves repetition", that's terrible - inheritance is not a code-reuse mechanism.
Matthew J Morrison
"inheritance is not a code-reuse mechanism". Firmly at odds with other folks opinions on OOP. Why do you say that? If it's not about reuse of features from superclass to subclass, what is it about?
S.Lott
"the code that governs that data is different" But it isn't.
S.Lott
http://littletutorials.com/2008/06/23/inheritance-not-for-code-reuse/ http://en.wikipedia.org/wiki/Implementation_inheritance
Matthew J Morrison
how is the code that governs the data NOT different if you can turn on the Django Admin site a manipulate the model directly, or if a new programmer starts on your project and doesn't know that he needs to extends ModelFormX in order to get all of the validation. Forcing that kind of development is a smell.
Matthew J Morrison
"There is some code reuse associated with inheritance" First link. "Inheritance is a way to compartmentalize and reuse code" Second link. Not sure what your point is. However, we use Delegation as well as Inheritance to assure that the rules occur once.
S.Lott
"...a new programmer starts on your project and doesn't know that he needs to extends ModelFormX in order to get all of the validation".... Not a code or a Django problem, but a project management problem. You really hate something. I can't tell what it is, though.
S.Lott
I'm not driven by hate but by passion. If Person needs to be able to "hop", then Person should NOT inherit that ability from Frog. Inheritance does involve code reuse, yes, but that is not a reason to use it. Also, if project management involves telling my developers "if you need to validate ModelX then you have to create a ModelForm and it must sub-class ...", then something there is wrong. If a Model requires some logic to happen when at the time it is saved, it needs to be in the model, there is really no way that anyone can argue against that. Why add ceremony when there needn't be any?
Matthew J Morrison
"If Person needs to be able to "hop", then Person should NOT inherit that ability from Frog" Terrible example. Does not show inheritance correctly at all. That's delegation; please don't conflate the two. "If a Model requires some logic to happen when at the time it is saved, it needs to be in the model" No. It has to be available to the model, perhaps via delegation. Perhaps via inheritance.
S.Lott
The Person -> Frog is not a terrible example, that is a PERFECT example of how inheritance is abused. I've seen far too many things done like that just because the code was already in Frog and was needed in Person. That's my point "CORRECT" inheritance is NOT about code reuse, like you said, delegation is the CORRECT thing to do in that example. If a model requires something to happen when it is saved the MODEL should DO it... not meaning that the code pysically is IN the model but that the Model itself decides to do it...not the Form, the Model. I think we're starting to say the same thing.
Matthew J Morrison
@Matthew J Morrison: "PERFECT example of how inheritance is abused"? Why is that so important? It's a terrible example because it's irrelevant to the question and the answer. Why is this so important? I'm lost.
S.Lott
@S.Lott - The comment that you made: "good-old inheritance saves repetition" is what prompted me to bring up the Frog -> Person example. My point is that I've got some code that I need to run when a Model is saved, if that code is in a ModelForm then inheritance is NOT the answer to re-use that code for other ModelForms, RESTful web services, and/or a batch jobs - ESPECIALLY if it ALWAYS needs to run. The answer is to just call Model.save() and let the model do it (using delegation or whatever other means, but it needs to happen from within the model rather than externally).
Matthew J Morrison
@Matthew J Morrison: "code that I need to run when a Model is saved" is not the same thing as validation. I don't get your point. Validation is only one of many business rules. Indeed an application is nothing more than a giant collection of business rules. Rules are all over the place. And validation goes in Forms. That's the way Django works, and it makes a tiny scrap of sense.
S.Lott
you cannot truly believe that a blanket statement like 'validation goes in forms' is true, that is ridiculous.. A form is not the only possibe way for data to get to a model. Speaking in broad MVC terms and in Django terms, an email address field should always be a valid email address that should be enforced by the model, not the code using the model. That's all I've got to say on this topic.
Matthew J Morrison
@ Matthew J Morrison: "you cannot truly believe". I read the Django documentation. I'm telling you how it works. I believe it works that way. Do you have evidence that the Django documentation is wrong?
S.Lott
@S.Lott see my first comment
Matthew J Morrison
@Matthew J Morrison: So, you're not complaining about the technical content in my answer. You're complaining about Django, itself. Why?
S.Lott
I would have to agree with Matthew... If it is the Django way to ensure correctness with ModelForms, thats a little silly... That is assuming that the only time Models are created is via forms, and not by any other means, such as creating a model via code... If I wanted to create a model via code, it would be created with Models, and note ModelForms... Therefor, if there is some save condition that needs to be true for a specific model, then it should be coded in the model, and not the form.
shawnjan
In my example, I need to check the name of the form against a list of names that it cannot be.. Forms would work fine for this, I could do it the forms way and get what I want from a Admin perspective... but if I was to create a model via code, and accidentally named it a name that it shouldn't be, then the model will still get created, despite what its limitations should be... Therefore, I believe this logic should exists at the model level.
shawnjan
+1  A: 

I'm not 100% sure, but I think you should be able to raise either a ValueError or a django.forms.ValidationError in your save() method in your model

def save(self):
    if yourvalidation:
        super(Model, self).save() #call super to actually do save
    else:
        raise #either ValueError or ValidationException

Again, i'm not sure if this will work in 1.1... but this is what I would try.

Matthew J Morrison
I'll try this out... Hopefully it works in 1.1 as this is the solution I want, I would really prefer not to have my save condition residing in a ModelForm.
shawnjan
@shawnjan - let me know how it goes, I hope this works.
Matthew J Morrison