views:

76

answers:

3

In django, I'm trying to do something like this:

# if form is valid ...
article = form.save(commit=False)
article.author = req.user

product_name = form.cleaned_data['product_name']
try:
 article.product = Component.objects.get(name=product_name)
except:
 article.product = Component(name=product_name)

article.save()
# do some more form processing ...

But then it tells me:

null value in column "product_id" violates not-null constraint

But I don't understand why this is a problem. When article.save() is called, it should be able the create the product then (and generate an id).

I can get around this problem by using this code in the except block:

product = Component(name=product_name)
product.save()
article.product = product

But the reason this concerns me is because if article.save() fails, it will already have created a new component/product. I want them to succeed or fail together.

Is there a nice way to get around this?

+4  A: 

The way the Django ManyToManyField works is that it creates an extra table. So say you have two models, ModelA and ModelB. If you did...

ModelA.model_b = models.ManyToManyField(ModelB)

What Django actually does behind the scenes is it creates a table... app_modela_modelb with three columns: id, model_a_id, model_b_id.

Hold that thought in your mind. Regarding the saving of ModelB, Django does not assign it an ID until it's saved. You could technically manually assign it an ID and avoid this problem. It seems you're letting django handle that which is perfectly acceptable.

Django has a problem then doing the M2M. Why? If ModelB doesn't have an id yet, what goes in the model_b_id column on the M2M table? The error for null product_id is more than likely a null constraint error on the M2M field, not the ModelB record id.

If you would like them to "succeed together" or "fail together" perhaps it's time to look into transactions. You, for example, wrap the whole thing in a transaction, and do a rollback in the case of a partial failure. I haven't done a whole lot of work personally in this area so hopefully someone else will be of assistance on that topic.

T. Stone
+1 for transaction
Davy8
looks to be well documented http://docs.djangoproject.com/en/dev/topics/db/transactions/
michael
I should have deleted that comment about m2m. Component is *not* a m2m field. It's just a `ForeignKey`. This doesn't really answer why the Component can't be created just before the Article is. The reason I bring this up is because that's how it normally works with forms. When you do `modelForm.save()` it will create whatever other objects are necessary in the process, and saves them all (unless you have `commit=False`). Anyway, I guess `transactions` are an OK alternative.
Mark
+1  A: 

You could get around this by using :

target_product, created_flag = Component.objects.get_or_create(name=product_name)
article.product = target_product

as I'm pretty sure get_or_create() will set the id of an object, if it has to create one.

Alternatively, if you don't mind empty FK relations on the Article table, you could add null=True to the definition.

thepeer
What if I don't care about the `created_flag`? Is there some python syntaxy goodness that allows me to omit that? Maybe a comma, but no 2nd val?
Mark
Underscore is quite common: target_product, _ = Component.object.get_or_create(...)I use double underscore, I reserve single underscore for i18n functions.
John Keyes
That underscore is quite ugly, don't you think? And dangerous, as you point out, if you use django i18n. There is no performance benefit in giving the flag a meaningless name, ie the unwanted variable still gets created, so I would give it a meaningful one, to be honest (and explicit).
thepeer
Just FYI, I discovered putting `[0]` on the end works quite well.
Mark
+1  A: 

There's little value in including a code snippet on transactions, as you should read the Django documentation to gain a good understanding.

John Keyes
You're right, but I was looking for a solution that didn't involve transactions at all. The documentation on transactions looks pretty clear.
Mark
Any reason for avoiding a transaction approach?
John Keyes