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