Is it possible to save children before parents?

73 views
Skip to first unread message

Mike Dewhirst

unread,
Feb 11, 2017, 5:55:52 PM2/11/17
to Django users
A mixture has a number of ingredients and the proportions should
(eventually) sum to 100%

Ingredients are substances connected to the mixture - also a substance
- via m2m relationships. Each m2m record carries an ingredient
proportion value.

If a proportion changes the sum total changes after saving the mixture
but cannot be displayed in the parent until the following save. I expect
I'm wrong but I cannot see any way in the Admin to update the parent
field in which the total is saved until after the second save.

Is it possible to save children before the parent so the sum can be
evaluated, the parent field updated and then the parent saved?

Thanks

Mike

Django 1.8
Python 2.7 and 3.5

Derek

unread,
Feb 12, 2017, 9:12:51 AM2/12/17
to Django users
Mike

Not sure I understand your scenario completely, but can you not just add a post-save trigger that updates the parent  field (in which the total is saved) after all the other saves have happened?

Mike Dewhirst

unread,
Feb 12, 2017, 6:47:29 PM2/12/17
to django...@googlegroups.com
On 13/02/2017 1:12 AM, Derek wrote:
> Mike
>
> Not sure I understand your scenario completely, but can you not just
> add a post-save trigger that updates the parent field (in which the
> total is saved) after all the other saves have happened?

Derek

That does not work and is one of the ways I have tried. I have also
tried pre-saving the ingredient proportions. [1]

It is all triggered by saving the mixture and all happens at the
database level not in the browser.

1. Start with ingredients summing to 100% (A = 50% and B = 50%)
- mixture M displays 100% in its total field
- A displays 50% (nicely displayed via __str__(self))
- B displays 50%

2. Adjust B to 40% and click [Save]
- mixture gets saved and checks ingredient proportions (before or
after it gets saved itself) and M = 100% because B has not yet been
saved and the database still has it at 50%.

3. B gets saved as a related object and B = 40%

4. Refresh the page and predictably nothing changes because the database
still has M = 100%, A = 50%, B = 40%.

5. Click [Save] again and the mixture checks proportions again except
this time B = 40% so M correctly becomes 90% in the database and is
correctly displayed.

I would love to adjust Substance_Ingredients._meta.verbose_name_plural
to display the total percentage and forget about storing it in the
mixture altogether. I only want a "live" display of the total to assure
the user about proportions.

I have looked at adjusting verbose_name_plural in the admin but I can't
figure out how to get access to the proportions to do the sums.

I have a horrible feeling I'm going to have to allocate some really
scarce brainspace to learning javascript. :-(

Mike

[1] I thought a pre-save would work but unfortunately not. If it
*should* work I must be doing something wrong. We really need to
pre-save the ingredients but it all happens in a single transaction
which I think must succeed or be rolled back.




>
> On Sunday, 12 February 2017 00:55:52 UTC+2, Mike Dewhirst wrote:
>
> A mixture has a number of ingredients and the proportions should
> (eventually) sum to 100%
>
> Ingredients are substances connected to the mixture - also a
> substance
> - via m2m relationships. Each m2m record carries an ingredient
> proportion value.
>
> If a proportion changes the sum total changes after saving the
> mixture
> but cannot be displayed in the parent until the following save. I
> expect
> I'm wrong but I cannot see any way in the Admin to update the parent
> field in which the total is saved until after the second save.
>
> Is it possible to save children before the parent so the sum can be
> evaluated, the parent field updated and then the parent saved?
>
> Thanks
>
> Mike
>
> Django 1.8
> Python 2.7 and 3.5
>
> --
> 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...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/4441fec6-783c-41d8-a6bf-63499cdfece2%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/4441fec6-783c-41d8-a6bf-63499cdfece2%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Antonis Christofides

unread,
Feb 13, 2017, 2:45:25 AM2/13/17
to django...@googlegroups.com
Hello Mike,

Could you paste (the necessary part of) your models.py? Otherwise it's not easy
to understand _exactly_ what you are doing, and the exact details are playing a
role here.

Regards,

Antonis

Antonis Christofides
http://djangodeployment.com

ludovic coues

unread,
Feb 13, 2017, 5:13:03 AM2/13/17
to django...@googlegroups.com
You could use a method instead of a property for the proportions. 

The cached_property decorator let you use the method like a property and will cache the results so it's only computed once per objects.


>> To post to this group, send email to django...@googlegroups.com
--
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...@googlegroups.com.

Melvyn Sopacua

unread,
Feb 13, 2017, 7:58:47 PM2/13/17
to django...@googlegroups.com

On Sunday 12 February 2017 09:55:16 Mike Dewhirst wrote:

> A mixture has a number of ingredients and the proportions should

> (eventually) sum to 100%

>

> Ingredients are substances connected to the mixture - also a substance

> - via m2m relationships. Each m2m record carries an ingredient

> proportion value.

>

> If a proportion changes the sum total changes after saving the mixture

> but cannot be displayed in the parent until the following save.

 

The trick is not to focus on saving, but on providing a consistent state when loading the model instance, similar as to what ImageField does with it's width and height fields.

 

Here's an example:

https://gist.github.com/melvyn-sopacua/1b4e4585ab15ffc176798c083470cf04

--

Melvyn Sopacua

Melvyn Sopacua

unread,
Feb 13, 2017, 8:15:33 PM2/13/17
to django...@googlegroups.com

P.S. I left two bugs in there, for now I'll leave them in as an exercise for the reader.

--

Melvyn Sopacua

Mike Dewhirst

unread,
Feb 14, 2017, 12:16:59 AM2/14/17
to django...@googlegroups.com
Derek, Antonis, Ludovic and Melvyn

I've got a lot happening in the code because chemical mixtures and
safety regulations are complex. I will attempt to radically simplify it
in a new minimal project and prove/disprove things and report back under
this thread.

Many thanks for the suggestions

Much appreciated

Mike
> --
> 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...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/10404615.9y43U2G7Fx%40devstation
> <https://groups.google.com/d/msgid/django-users/10404615.9y43U2G7Fx%40devstation?utm_medium=email&utm_source=footer>.

Mike Dewhirst

unread,
Feb 14, 2017, 11:15:41 PM2/14/17
to django...@googlegroups.com
Logically, this is a tail-chasing exercise. The total sum of ingredient
proportions is calculated from a query of ingredients. It runs when
saving the mixture. It can only get previously saved data.

Aaaargh!!!

I forgot to tell you that the ingredients are inlines and proportions
get saved when the mixture is saved.

If there is a way to save inlines *before* saving the main record the
problem might be solved.

Otherwise the only solution I can see is to inject some javascript into
the Admin to read the proportion values and update the sum field just
prior to saving the lot.

At this point I think a double save is easier than js

Thanks again

Mike

Antonis Christofides

unread,
Feb 15, 2017, 2:01:34 AM2/15/17
to django...@googlegroups.com
Hi,

the problem you are having has some resemblance to a problem I once faced,
http://stackoverflow.com/questions/33500336/checking-integrity-of-manytomanyfield.
It's not the same; my problem was to check the integrity of the "children",
whereas you seem to have a problem with the admin. However it keeps coming to me
during the thread so I thought I'd mention it.

Regards,

Antonis

Antonis Christofides
http://djangodeployment.com



Mike Dewhirst

unread,
Feb 15, 2017, 2:53:28 AM2/15/17
to django...@googlegroups.com
On 15/02/2017 6:01 PM, Antonis Christofides wrote:
> Hi,
>
> the problem you are having has some resemblance to a problem I once faced,
> http://stackoverflow.com/questions/33500336/checking-integrity-of-manytomanyfield.
> It's not the same; my problem was to check the integrity of the "children",
> whereas you seem to have a problem with the admin. However it keeps coming to me
> during the thread so I thought I'd mention it.

Antonis

AFAICT the problem is really not a problem at all. It is data integrity
protection which I should not be trying to circumvent. The save, plus
any pre-save and post-save activity takes place in a single transaction
involving the main record and all children. Only when that transaction
is committed and succeeds is the database actually updated. That says
the children and master are updated more or less simultaneously.

I think it has to be done in two transactions. Two saves.

Hence, I need to find a way of updating the children in a first
transaction which is committed and updates the database before the
master can find the new proportion values when it goes looking during
the next save (or second transaction).

If I wasn't using the admin, I could do that in my own forms without too
much trouble.

Or javascript might look at the in-browser children values and change
the sum in the master record in the browser. At that point a save puts
the precalculated sum into the database at the same time as the changed
values in the children records.

I looked at javascript once and nearly vomited. We'll save() twice until
we get some funding then I'll hire a js person to let me off the hook ;-)

Thanks again

Cheers

Mike

Mike Dewhirst

unread,
Feb 15, 2017, 3:18:22 AM2/15/17
to django...@googlegroups.com
On 14/02/2017 12:14 PM, Melvyn Sopacua wrote:
>
> P.S. I left two bugs in there, for now I'll leave them in as an
> exercise for the reader.
>

Three bugs ... and 2 questions:

1. You obviously forgot the sugar!

2. With that proportion of Earl Gray the tea would have been far too strong.

3. There might be 5% discrepancy in the total!

Now for the questions ...

class CalculatedPercentageField(models.PositiveSmallIntegerField):
pct_model = None
pct_field_name = None

def __init__(self, pct_model: models.Model, pct_field_name='percentage',
*args, **kwargs):
self.pct_model = pct_model
self.pct_field_name = pct_field_name
super().__init__(*args, **kwargs)
...


1. What does "pct_model: models.Model," do? I haven't seen that colon
before.

2. Will it work when used via the Admin and everything gets saved at
once in a single transaction?

Thanks very much for taking all that effort

Cheers

Mike

Melvyn Sopacua

unread,
Feb 15, 2017, 5:24:04 AM2/15/17
to django...@googlegroups.com

On Wednesday 15 February 2017 19:17:55 Mike Dewhirst wrote:

> On 14/02/2017 12:14 PM, Melvyn Sopacua wrote:

> > P.S. I left two bugs in there, for now I'll leave them in as an

> > exercise for the reader.

>

> Three bugs ... and 2 questions:

>

> 1. You obviously forgot the sugar!

 

That's part of the first bug: it allows 2 of the same ingredients to be added to one mixture. Normally, you would merge them into one or disallow it with a unique_together on the through model.

 

> 2. With that proportion of Earl Gray the tea would have been far too

> strong.

 

Haha, true.

> 3. There might be 5% discrepancy in the total!

 

Nope, should be correct. But the other bug is that it totals all mixtures, instead of only the instance of the mixture. This is because with through models you cannot use the related manager and I didn't add an explicit filter to compensate.

 

> Now for the questions ...

>

> class CalculatedPercentageField(models.PositiveSmallIntegerField):

> pct_model = None

> pct_field_name = None

>

> def __init__(self, pct_model: models.Model,

> pct_field_name='percentage', *args, **kwargs):

> self.pct_model = pct_model

> self.pct_field_name = pct_field_name

> super().__init__(*args, **kwargs)

> ...

>

>

> 1. What does "pct_model: models.Model," do? I haven't seen that colon

> before.

 

This is me not having written python2 compatible code for a while. It's a python3.4+ supported type hint that makes it easier for the IDE to infer supported methods and properties.

 

> 2. Will it work when used via the Admin and everything gets saved at

> once in a single transaction?

 

It should yes. Note that save is always one step behind what is presented. In practice this should not pose a problem and you might even put a save with update_fields in update_total() if old and new value differ, but this may trigger a can of worms if you ever put a save-related signal on the model.

 

--

Melvyn Sopacua

Reply all
Reply to author
Forward
0 new messages