简体   繁体   中英

How to execute two update statements in one transaction so they won't run into unique constraint in Django ORM?

Given models

from django.db import models


class RelatedTo(models.Model):
    pass


class Thing(models.Model):
    n = models.IntegerField()
    related_to = models.ForeignKey(RelatedTo, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['n', 'related_to'],
                name='unique_n_per_related_to'
            )
        ]

and

>>> r = RelatedTo.objects.create()
>>> thing_zero = Thing.objects.create(related_to=r, n=0)
>>> thing_one = Thing.objects.create(related_to=r, n=1)

I want to switch their numbers ( n ).

In update method of my serializer (drf) I was trying to

@transaction.atomic
def update(self, instance, validated_data):
    old_n = instance.n
    new_n = validated_data['n']

    Thing.objects.filter(
        related_to=instance.related_to,
        n=new_n
    ).update(n=old_n)
    return super().update(instance, validated_data)

but it still runs into constraint.

select_for_update doesn't help either.

Is it possible not to run into this DB constraint using Django ORM or do I have to run raw sql to achieve that?

Django==3.1.2
postgres:12.5

Error

duplicate key value violates unique constraint "unique_n_per_related_to"
DETAIL:  Key (n, related_to)=(1, 1) already exists.

I wasn't able to resolve this issue neither with bulk_update nor with raw sql.

stmt = f"""
            update {to_update._meta.db_table} as t
            set n = i.n
            from (values
                ('{to_update.id}'::uuid, {n}),
                ('{method.id}'::uuid, {n})
            ) as i(id, n)
            where t.id = i.id
            """

with connection.cursor() as cur:
    cur.execute(stmt)

The only solution for this problem is making the column nullable and write 3 times to the table which physically hurts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM