Thursday, June 25, 2015

Re: Atomic block termination question

Carl

On 25/06/2015 2:34 AM, Carl Meyer wrote:
> Hi Mike,
>
> On 06/24/2015 02:16 AM, Mike Dewhirst wrote:
>> On 24/06/2015 4:43 PM, Mike Dewhirst wrote:
>>> When saving a model I'm getting a TransactionManagementError - You can't
>>> execute queries until the end of the 'atomic' block
>>>
>>> Ticket #21540 seems fairly explicit at least where Postgres is
>>> concerned. TransactionManagementError prevents what I want to do and I'm
>>> not a nuclear expert.
>>>
>>> How do I terminate the save() method code in that atomic block and then
>>> immediately execute my queries?
>
> I'm afraid this description of what you're trying to do is too vague to
> be useful. Maybe some sample code would help?
>
> TransactionManagementError is a symptom, not a cause. It means that a
> database error occurred inside a transaction, which leaves the
> transaction in an unpredictable state, so Postgres wants you to roll
> back the transaction (or roll back to a savepoint prior to the error)
> before issuing any more database queries.

Ok. I thought from reading the ticket that I was trying to do something
illegal in Postgres - that is issuing a query within a transaction which
needed to be finalised or rolled back. I took it as a symptom or signal
and think I understand that Postgres is somewhat more rigorous in this
regard than MySQL.

>
> Possible solutions include:
>
> a) Figuring out why there was a database error, and fixing it so it
> doesn't occur.

I separated out all the pre and post-save stuff without the offending
queries and put them into ...

def save(self, *args, **kwargs):
self._pre_save() # nothing tricky here
super(Substance, self).save(*args, **kwargs)
self._post_save() # nothing tricky here

... which stopped the TransactionManagementError and everything worked
on existing substances which already had the necessary 1:1 relations AND
it kinda "worked" when I [saved as new] except obviously the 1:1
relations were not created.

... then did a _post_post_save() with the offending queries ...

def _post_post_save(self):
if self.physical_state == SOLID:
Solid.objects.get_or_create(substance=self)
elif self.physical_state == LIQUID:
Liquid.objects.get_or_create(substance=self)
elif self.physical_state == GAS:
Gas.objects.get_or_create(substance=self)
elif self.physical_state == AEROSOL:
Aerosol.objects.get_or_create(substance=self)
Aquatic.objects.get_or_create(substance=self)
Tox.objects.get_or_create(substance=self)
Terrestrial.objects.get_or_create(substance=self)
if self.terrestrial:
# We can't do this in terrestrial.save() and it needs
# to be recomputed on every save
self.terrestrial.set_soil_m_factor()
self.terrestrial.set_vertebrate_m_factor()
self.terrestrial.set_invertebrate_m_factor()

... which as I said is now called from substance.clean(). I realise
clean() is called before save() but that's all I can think of at the
moment. Those m_factors are unlikely to change once the concentration
values (EC50, LD50 etc) upon which they are based are set.

>
> b) Wrapping the code that might cause a database error in its own
> `transaction.atomic` block, so on error that bit of code is rolled back
> and later queries within the same transaction can go forward.

That sounds like nuclear physics to me. I could probably follow a recipe
but might have trouble figuring out when to use it.

Thanks for listening

Mike
>
>> I have implemented a workaround but not sure if it is the best way. Any
>> comment appreciated ...
>>
>> In the model's clean() method I test for self.pk and if true, execute
>> the queries which previously caused the problem. This seems to work but
>> hasn't had any testing in production.
>
> Again it's hard to tell without seeing code or traceback, but it sounds
> like probably what you've done here is fix the condition that was
> causing the error in the first place (that is, solution (a) above). It
> sounds like you were probably trying to do some related-model queries
> with an unsaved model, and now you've guarded those queries to only
> occur if the model is saved. If so, that's not a workaround, it's the
> best solution.
>
>>> I need the save() to complete so I can get_or_create some 1:1 records
>>> belonging to the model being saved.
>
> Again, sample code would really illuminate what you're trying to do.
>
> Carl
>

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/558BB38E.8060104%40dewhirst.com.au.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment