You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django创建Location模型时出现重复键错误的问题排查

问题描述

我定义了一个Location模型,其中city字段关联City模型,数据库已预先填充国家、州/省和城市数据,创建新Location时只需选择对应城市即可。

Location模型代码:

class Location(models.Model):
    name = models.CharField(max_length=50, default=None, null=True, blank=True)
    street = models.CharField(max_length=100)
    additional = models.CharField(max_length=100, null=True, blank=True)
    city = models.OneToOneField(City, on_delete=models.CASCADE, related_name="cities")
    zip_code = models.CharField(max_length=30)
    phone = models.CharField(max_length=15, null=True, blank=True)

    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="created at")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="updated at")

    class Meta:
        verbose_name = "location"
        verbose_name_plural = "locations"
        db_table = "locations"
        ordering = ["zip_code"]

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return self.slug

我尝试通过Patch请求中的update_or_create方法更新或创建Location,视图代码如下:

class UpdateUserSettings(generics.UpdateAPIView):
    permission_classes = [permissions.IsAuthenticated]

    serializer_class = SettingsSerializer
    name = 'user-settings'

    def get_queryset(self):
        id = self.kwargs['pk']
        return UserSettings.objects.all().filter(user=id)

    def patch(self, request, *args, **kwargs):
        user = User.objects.get(id=self.request.data.get('user'))
        settings = UserSettings.objects.get(user=user)
        form = self.request.data.get('form')
        if form == 'editEssentialsForm':
            email = self.request.data.get('email')
        elif form == 'editPaymentsForm':
            gateway = Gateway.objects.get(name=request.data.get("gateway"))
            settings.gateway = gateway
            name = "user-" + str(user) + "'s billing address"
            street = request.data.get("billing_street")
            additional = request.data.get("billing_additional")
            city = City.objects.get(id=request.data.get("billing_city"))
            zipcode =  request.data.get("billing_zip")
            Location.objects.update_or_create(name=name, street=street, additional=additional, city=city, zip_code=zipcode, phone=settings.phone, user=user)
            return Response("Settings updated successfully", status=HTTP_200_OK)

执行创建新Location时出现以下错误:

django.db.utils.IntegrityError: duplicate key value violates unique constraint "locations_city_id_key"
DETAIL:  Key (city_id)=(1) already exists.

数据库中已有一个使用同一城市的Location,想知道为何无法创建多个同城市的Location,哪里操作有误?

问题原因

核心问题出在Location模型的city字段定义上:你使用了OneToOneField,这种关联关系的特性是一个City实例只能对应一个Location实例,Django会自动在数据库的locations表中为city_id字段添加唯一约束。当你尝试创建第二个关联同一City的Location时,就会触发唯一约束冲突,抛出上述错误。

解决方案

要允许多个Location关联同一个City,需要将OneToOneField改为ForeignKey(多对一关联,多个位置属于同一个城市):

  1. 修改Location模型中的city字段:
city = models.ForeignKey(City, on_delete=models.CASCADE, related_name="locations")
  • 同时建议把related_name从"cities"改为"locations",语义更准确(表示一个城市对应的所有位置)。
  1. 执行数据库迁移命令,更新数据库结构:
python manage.py makemigrations
python manage.py migrate

迁移完成后,数据库会移除city_id字段的唯一约束,你就可以创建多个关联同一城市的Location了。

另外补充一点:你的update_or_create用法可能存在逻辑问题——当前是把所有字段作为匹配条件,意味着只要任何一个字段(比如street、zip_code)变化,就会创建新的Location。如果你的需求是为同一个用户更新或创建唯一的账单地址,应该把user作为唯一匹配条件,其他字段放在defaults参数里:

Location.objects.update_or_create(
    user=user,
    defaults={
        'name': name,
        'street': street,
        'additional': additional,
        'city': city,
        'zip_code': zipcode,
        'phone': settings.phone
    }
)

这样可以确保每个用户只有一个账单地址,避免重复创建。


内容的提问来源于stack exchange,提问作者EagerLearner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:35:12