#35848 closed Bug (invalid)
Problem with UniqueConstraint on fields, one of which allows NULL value.
Reported by: | Андрей | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 5.1 |
Severity: | Normal | Keywords: | UniqueConstraint, nullable, null |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
UniqueConstraint does not work correctly for several fields, one of which is nullable.
class MyModel(models.Model): id = models.BigAutoField(primary_key=True, editable=False) name = models.CharField(max_length=1000, verbose_name="Name") measure = models.ForeignKey( Measure, related_name="mymodel_measure", on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Measure", ) class Meta: constraints = [ models.UniqueConstraint( fields=["name"], name="mymodel_unique_name_measure_null", condition=Q(measure__isnull=True), violation_error_message="Error message for null", ), models.UniqueConstraint( fields=["name", "measure"], name="mymodel_unique_name_measure_not_null", condition=Q(measure__isnull=False), violation_error_message="Error message for not null", ), ]
Database - Postgres 15.
The error is as follows: if there is a record with the field measure = not null, you can create a new record with the same name and measure = null. And if there is an entry with measure = null, then a new entry with the same name and measure = not null does not work.
From the Postgres side, Constraints are created and working correctly.
Error message:
{ "error": "{'name': [ErrorDetail(string='The name of the mymodel with this Name already exists.', code='unique')]}" }
Attachments (5)
Change History (13)
comment:1 by , 2 months ago
Component: | Uncategorized → Database layer (models, ORM) |
---|---|
Has patch: | set |
comment:2 by , 2 months ago
Has patch: | unset |
---|
comment:3 by , 2 months ago
Resolution: | → worksforme |
---|---|
Status: | new → closed |
comment:4 by , 2 months ago
You are right that when Django is running through the SHELL, the error is not reproduced (I checked it too after your message).
However, there is an error when creating an object using the rest api.
The order is as follows:
- Create any measure object (m1. This can be created using shell ).
- Create mymodel with the parameter measure = null (name="test". This can be created using shell ).
- Create mymodel from rest api (name="test", parameter=m1. This should be created via the viewset, in our case the rest api via postman/bruno ).
I get the error:
{ "name": [ "my model with that name already exists." ] }
My scripts:
models.py
from django.db import models from django.db.models import Q class Measure(models.Model): id = models.AutoField( primary_key=True, verbose_name="Key", ) code = models.CharField( max_length=5, unique=True, verbose_name="Code", error_messages={"unique": "Err msg."}, ) def __str__(self): return self.code class MyModel(models.Model): id = models.BigAutoField(primary_key=True, editable=False) name = models.CharField(max_length=1000, verbose_name="Name") measure = models.ForeignKey( Measure, related_name="mymodel_measure", on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Measure", ) def __str__(self): return self.name class Meta: constraints = [ models.UniqueConstraint( fields=["name"], name="mymodel_unique_name_measure_null", condition=Q(measure__isnull=True), violation_error_message="Error message for null", ), models.UniqueConstraint( fields=["name", "measure"], name="mymodel_unique_name_measure_not_null", condition=Q(measure__isnull=False), violation_error_message="Error message for not null", ), ]
views.py
from rest_framework import viewsets from directories.serializers import MyModelSerializer from .models import MyModel class MyModelViewSet(viewsets.ModelViewSet): queryset = MyModel.objects.select_related("measure").all() serializer_class = MyModelSerializer
serializers.py
from rest_framework import serializers from directories.models import MyModel class MyModelSerializer(serializers.ModelSerializer): class Meta: model = MyModel fields = "__all__"
urls.py
from django.urls import path, include from rest_framework.routers import DefaultRouter from directories.views import MyModelViewSet router = DefaultRouter() router.register("mymodel", MyModelViewSet) urlpatterns = [ path("d/", include(router.urls)), ]
Next in postman or bruno create object (step 3):
post method, body:
{ "name": "test", "measure": 1 }
and I get an error:
{ "name": [ "my model with that name already exists." ] }
by , 2 months ago
by , 2 months ago
Attachment: | serializers.py added |
---|
by , 2 months ago
by , 2 months ago
by , 2 months ago
Attachment: | settings.py added |
---|
comment:5 by , 2 months ago
Resolution: | worksforme |
---|---|
Status: | closed → new |
comment:6 by , 2 months ago
Unless you can show that this is a bug in Django, you will need to report this to Django REST framework: https://github.com/encode/django-rest-framework/issues
comment:7 by , 2 months ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
comment:8 by , 2 months ago
The error falls from serializer.is_valid (it is rest_framework.exceptions.ValidationError):
>>> from directories.views import MyModelViewSet >>> viewset = MyModelViewSet() >>> new_object = viewset.create(data={'name': 'test', 'measure': 1}) Traceback (most recent call last): File "<console>", line 1, in <module> File "....views.py", line 15, in create serializer.is_valid(raise_exception=True) File "....venv\Lib\site-packages\rest_framework\serializers.py", line 231, in is_valid raise ValidationError(self.errors) rest_framework.exception s.ValidationError: {'name': [ErrorDetail(string='my model with this Name already exists.', code='unique')]}
I couldn't replicate - could you share a script?
I am using postgres 15 and Django 5.1.2