#30129: Allow creating models with fields values as a Subquery() with F()
expressions
-------------------------------------+-------------------------------------
Reporter: Charlie McBride | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: 2.1
(models, ORM) |
Severity: Normal | Resolution:
Keywords: Subquery, F, Query | Triage Stage:
Expressions | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Charlie McBride:
Old description:
> I understand why F() expressions are generally disallowed inside insert
> statements, the columns you are referencing don't yet exist, so it
> wouldn't make any sense to do so. However, if you are performing an
> insert with a Subquery (as in the example below), it's possible to have
> otherwise valid statements rejected because of the blanket blacklist of
> F() expressions during inserts.
>
> For example:
>
> given the following models:
>
> {{{
> class Item(models.Model):
> pass
>
> class ItemVersion(models.Model):
> item = models.ForeignKey(Item, related_name='versions')
> version_number = models.IntegerField(default=0)
>
> class Meta:
> unique_together = ('item', 'version_number',)
> }}}
>
> I would like to be able to do the following operation to mitigate race
> conditions as described here
> (https://docs.djangoproject.com/en/2.1/ref/models/expressions/#avoiding-
> race-conditions-using-f)
>
>
> {{{
> item = Item.objects.create()
> # arbitrary number of other items created/destroyed etc
> item_version_2 = ItemVersion.objects.create(
> item=item,
> version_number=Subquery(
> item.versions.order_by('- version_number').annotate(
> max_version_number=Coalesce(Max('version_number'), 0)
> ).annotate(
> new_version_number=F('max_version_number) + 1
> ).values('new_version_number')[:1]
> )
> )
> }}}
>
> As written, I would expect the F() in the inner expression to always be
> resolvable, because it is in a Subquery (and not the result of an
> insert). However this query is blocked by the compiler because "F()
> expressions can only be used to update, not to insert." Would it be
> possible to allow F expressions in a Subquery even if it is being used in
> an insert? Is there an edge case that I'm missing that caused the team to
> not consider this?
New description:
I understand why F() expressions are generally disallowed inside insert
statements, the columns you are referencing don't yet exist, so it
wouldn't make any sense to do so. However, if you are performing an insert
with a Subquery (as in the example below), it's possible to have otherwise
valid statements rejected because of the blanket blacklist of F()
expressions during inserts.
For example:
given the following models:
{{{
class Item(models.Model):
pass
class ItemVersion(models.Model):
item = models.ForeignKey(Item, related_name='versions')
version_number = models.IntegerField(default=0)
class Meta:
unique_together = ('item', 'version_number',)
}}}
I would like to be able to do the following operation to mitigate race
conditions as described here
(https://docs.djangoproject.com/en/2.1/ref/models/expressions/#avoiding-
race-conditions-using-f)
{{{
item = Item.objects.create()
# arbitrary number of other items created/destroyed etc
item_version_2 = ItemVersion.objects.create(
item=item,
version_number=Subquery(
item.versions.order_by('-version_number').annotate(
max_version_number=Coalesce(Max('version_number'), 0)
).annotate(
new_version_number=F('max_version_number) + 1
).values('new_version_number')
)
)
}}}
As written, I would expect the F() in the inner expression to always be
resolvable, because it is in a Subquery (and not the result of an insert).
However this query is blocked by the compiler because "F() expressions can
only be used to update, not to insert." Would it be possible to allow F
expressions in a Subquery even if it is being used in an insert? Is there
an edge case that I'm missing that caused the team to not consider this?
--
--
Ticket URL: <https://code.djangoproject.com/ticket/30129#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/068.e7d8ab34767f216483a27c839767919f%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.