Django 1.8:如何实现Char字段区分大小写的联合唯一约束
解决Django 1.8联合唯一约束允许大小写不同条目保存的问题
问题根源
你用unique_together设置了联合唯一约束,但很多数据库(比如MySQL)默认的字符集排序规则是大小写不敏感的(比如utf8_general_ci),所以数据库会把central和Central当成同一个值,导致保存时触发重复报错。
两种解决方案
方案1:修改字段的数据库排序规则(最靠谱)
直接给room_name字段指定大小写敏感的排序规则,让数据库层面区分不同大小写的字符串,同时保留联合唯一约束。
修改models.py里的room_name字段:
room_name = models.CharField( verbose_name=_('room_name'), max_length=100, null=False, blank=False, default="", db_collation='utf8_bin' # MySQL用这个,PostgreSQL换成C或POSIX )
- MySQL选
utf8_bin:按字节严格比较,大小写完全区分。 - PostgreSQL选
C或POSIX:这两个排序规则是大小写敏感的。
改完后执行迁移:
python manage.py makemigrations python manage.py migrate
方案2:模型层加自定义验证(辅助用)
如果暂时动不了数据库排序规则,可以在模型的clean方法里加自定义检查,但这种方法防不了并发写入的冲突,只能作为补充。
修改Tags模型(注意补全继承和元组格式):
from django.core.exceptions import ValidationError class Tags(models.Model): # 原代码漏了继承models.Model,必须加上 Rooms = models.ForeignKey(Rooms, related_name='rooms') room_name = models.CharField( verbose_name=_('room_name'), max_length=100, null=False, blank=False, default="", ) def clean(self): # 检查同Rooms下是否已有完全匹配(区分大小写)的room_name exists = Tags.objects.filter( Rooms=self.Rooms, room_name__exact=self.room_name ).exclude(pk=self.pk).exists() if exists: raise ValidationError("Tags with this Rooms and Aws room_name already exists.") super(Tags, self).clean() class Meta: unique_together = (('Rooms', 'room_name'),) # 原代码少了逗号,得补成正确的元组格式
注意点
- 优先用方案1,从数据库层面解决问题最彻底,方案2只能做辅助验证。
- 执行迁移前记得备份数据库,避免数据出问题。
- 如果用SQLite,默认也是大小写不敏感的,需要调整连接参数或者用
COLLATE BINARY来实现区分大小写。
内容的提问来源于stack exchange,提问作者Vaibhav
相关产品推荐
相关产品推荐

