在 Django 中,如何找到违反唯一一起约束的行?
Posted
技术标签:
【中文标题】在 Django 中,如何找到违反唯一一起约束的行?【英文标题】:In Django, how to find rows which would violate a unique together constraint? 【发布时间】:2018-12-31 10:29:33 【问题描述】:对于以下 API 端点,
import json
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponseBadRequest
from django.views.decorators.http import require_POST
from lucy_web.models import UserApn
@login_required
@require_POST
def save_apn(request, version):
player_id = json.loads(request.body).get('player_id')
if player_id:
UserApn.objects.get_or_create(user=request.user, player_id=player_id)
return JsonResponse('status': 'success')
else:
return HttpResponseBadRequest()
这是底层模型:
from django.contrib.auth.models import User
from django.db import models
from .timestamped_model import TimeStampedModel
class UserApn(TimeStampedModel):
user = models.ForeignKey(User)
player_id = models.CharField(max_length=255)
对get_or_create()
的调用引发了一些MultipleObjectsReturned
错误。为了解决这个问题,我想对user
和player_id
施加unique_together
约束。但是,首先,我必须编写一个数据迁移,以消除违反此唯一共同约束的行。
我如何编写一个选择这些的查询?目前已提出以下建议:
def remove_duplicate_apns(apps, schema_editor):
UserApn = apps.get_model('lucy_web', 'UserApn')
previous_user_id = None
previous_player_id = None
for apn in UserApn.objects.all().order_by('user_id', 'player_id'):
if apn.user_id == previous_user_id and apn.player_id == previous_player_id:
print(f'deleting apn (id: apn.id)')
apn.delete()
else:
previous_user_id = apn.user_id
previous_player_id = apn.player_id
不过,这似乎也可以在单个查询中完成。
更新
我发现可以将user
和player_id
这两个字段传递给.values()
,然后使用.distinct()
检查重复项。例如,以下测试通过:
from django.test import TestCase
from django.contrib.auth.models import User
from myapp.models import UserApn
class UserApnTest(TestCase):
def test_1(self):
user = User.objects.create_user(username='jayz')
apn1 = UserApn.objects.create(user=user, player_id='foo')
apn2 = UserApn.objects.create(user=user, player_id='foo')
apn3 = UserApn.objects.create(user=user, player_id='bar')
self.assertEqual(
len(UserApn.objects.values('user', 'player_id')) -
len(UserApn.objects.values('user', 'player_id').distinct()), 1)
但是,问题仍然存在,此输出是带有user_id
和player_id
的字典,但原始id
丢失了,因此我不能随后get()
重复对象并删除它们。我怎样才能做类似的事情但保留对重复对象的引用?
【问题讨论】:
【参考方案1】:我设法将重复的UserApn
s 分组到以下查询集中:
UserApn.objects.all().difference(UserApn.objects.distinct('user', 'player_id'))
请注意,将多个参数传递给 distinct()
仅适用于 PostgreSQL。
【讨论】:
“仅适用于 PostgreSQL”自从我开始使用它以来,它一直是一个非常令人满意(并且经常出现)的短语。以上是关于在 Django 中,如何找到违反唯一一起约束的行?的主要内容,如果未能解决你的问题,请参考以下文章
django:IntegrityError:重复键值违反唯一约束
Postrgersql+Django:NULL值导致IntegrityError:重复键值违反唯一约束
Django Rest Framework:重复键值违反唯一约束
django update_or_create 得到“重复键值违反唯一约束”
django.db.utils.IntegrityError:重复键值违反唯一约束“auth_permission_pkey”