Django – duplicate key error after trying to create a new model

Question:

I have a location model and it has a city field, which is a foreign key. The database is already filled with all the countries, states and cities so when I want to create a new location I will just select the city, which is connected to a state and the state is connected to a country.

Here is the location model:

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

I am trying to update a location or create it if it does not exists yet and here is the code I use to do that. It’s part of a patch request:


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)

When I run the code to create the new location I get the following error:

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

There is one other location in the database with the same city so my question is why can I not have two locations with the same city which is what this error is telling me if I’m not wrong?

What am I doing wrong please?

Asked By: EagerLearner

||

Answers:

You have set:

city = models.OneToOneField(City, on_delete=models.CASCADE, related_name="cities")

An OneToOneField ensures that there should be unique city in your locations table. You should use ForeignKey if you want to add multiple locations that have same city.

Example:

city = models.ForeignKey(City, on_delete=models.CASCADE, related_name="cities")
Answered By: Rohit Rahman

Yes, Postgres uses what it calls a sequence 10 object to track the next PK to assign. If you restore the database and that restore process doesn’t include the related sequence object, you end up in exactly this situation.

You can use the ALTER SEQUENCE 47 statement (with the RESTART parameter) in psql to reset the sequence to the desired value.

You also want to ensure that your pg_dump command is written to include all sequences in your backup to prevent this from happening in the future.

Answered By: August Infotech