sam*_*per 76 database django django-models django-forms django-admin
假设我的models.py是这样的:
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
Run Code Online (Sandbox Code Playgroud)
我只想要我的一个Character实例is_the_chosen_one == True和其他所有实例is_the_chosen_one == False.如何才能最好地确保这种唯一性约束得到尊重?
考虑到尊重数据库,模型和(管理员)表单级别约束的重要性的答案的最高分!
Ada*_*dam 55
每当我需要完成这个任务时,我所做的就是覆盖模型的save方法,并检查是否有其他模型已经设置了标志(并将其关闭).
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
def save(self, *args, **kwargs):
if self.is_the_chosen_one:
try:
temp = Character.objects.get(is_the_chosen_one=True)
if self != temp:
temp.is_the_chosen_one = False
temp.save()
except Character.DoesNotExist:
pass
super(Character, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
Ell*_*val 26
我会覆盖模型的save方法,如果你将boolean设置为True,请确保所有其他方法都设置为False.
from django.db import transaction
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
def save(self, *args, **kwargs):
if not self.is_the_chosen_one:
return super(Character, self).save(*args, **kwargs)
with transaction.atomic():
Character.objects.filter(
is_the_chosen_one=True).update(is_the_chosen_one=False)
return super(Character, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
我尝试编辑Adam的类似答案,但因为改变了原来的答案而被拒绝了.这种方式更简洁,更有效,因为在单个查询中完成对其他条目的检查.
sau*_*ook 25
我没有使用自定义模型清理/保存,而是创建了一个覆盖方法的自定义字段.反而提高了一个错误,如果另一场是的,我所做的所有其他领域,如果它是.如果字段是并且没有其他字段,则不是引发错误,而是将字段保存为pre_savedjango.db.models.BooleanFieldTrueFalseTrueFalseTrueTrue
fields.py
from django.db.models import BooleanField
class UniqueBooleanField(BooleanField):
def pre_save(self, model_instance, add):
objects = model_instance.__class__.objects
# If True then set all others as False
if getattr(model_instance, self.attname):
objects.update(**{self.attname: False})
# If no true object exists that isnt saved model, save as True
elif not objects.exclude(id=model_instance.id)\
.filter(**{self.attname: True}):
return True
return getattr(model_instance, self.attname)
# To use with South
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^project\.apps\.fields\.UniqueBooleanField"])
Run Code Online (Sandbox Code Playgroud)
models.py
from django.db import models
from project.apps.fields import UniqueBooleanField
class UniqueBooleanModel(models.Model):
unique_boolean = UniqueBooleanField()
def __unicode__(self):
return str(self.unique_boolean)
Run Code Online (Sandbox Code Playgroud)
Cem*_*nel 14
在 Django 2.2 版之后,将这种约束添加到您的模型中会更简单。可以直接使用UniqueConstraint.condition。Django 文档
只需class Meta像这样覆盖您的模型:
class Meta:
constraints = [
UniqueConstraint(fields=['is_the_chosen_one'], condition=Q(is_the_chosen_one=True), name='unique_is_the_chosen_one')
]
Run Code Online (Sandbox Code Playgroud)
以下解决方案有点难看但可能有效:
class MyModel(models.Model):
is_the_chosen_one = models.NullBooleanField(default=None, unique=True)
def save(self, *args, **kwargs):
if self.is_the_chosen_one is False:
self.is_the_chosen_one = None
super(MyModel, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
如果将is_the_chosen_one设置为False或None,则它将始终为NULL.您可以根据需要使用NULL,但只能有一个True.
为了达到收支平衡,我发现其中一些问题成功解决了同一个问题,每个问题都适用于不同的情况:
我会选:
@semente:尊重数据库,模型和管理表单级别的约束,同时尽可能地覆盖Django ORM.而且它可以大概一个内部使用 through的表ManyToManyField的unique_together情况.(我会检查并报告)
class MyModel(models.Model):
is_the_chosen_one = models.NullBooleanField(default=None, unique=True)
def save(self, *args, **kwargs):
if self.is_the_chosen_one is False:
self.is_the_chosen_one = None
super(MyModel, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)@Flyte:仅在一个额外时间点击数据库并接受当前条目作为所选条目.干净优雅.
from django.db import transaction
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
def save(self, *args, **kwargs):
if not self.is_the_chosen_one:
# The use of return is explained in the comments
return super(Character, self).save(*args, **kwargs)
with transaction.atomic():
Character.objects.filter(
is_the_chosen_one=True).update(is_the_chosen_one=False)
# The use of return is explained in the comments
return super(Character, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)其他解决方案不适合我的情况,但可行:
@nemocorp正在覆盖clean执行验证的方法.但是,它不报告哪个模型是"那个",这不是用户友好的.尽管如此,这是一个非常好的方法,特别是如果有人不打算像@Flyte那样具有攻击性.
@ saul.shanabrook和@Thierry J.会创建一个自定义字段,可以将任何其他"is_the_one"条目更改为False或者引发一个ValidationError.我只是不愿意为我的Django安装提供新功能,除非它是非常必要的.
@daigorocub:使用Django信号.我发现它是一种独特的方法,并提供了如何使用Django信号的提示.但是我不确定这是否严格地说 - "正确"使用信号,因为我不能将此过程视为"解耦应用程序"的一部分.
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
def save(self, *args, **kwargs):
if self.is_the_chosen_one:
qs = Character.objects.filter(is_the_chosen_one=True)
if self.pk:
qs = qs.exclude(pk=self.pk)
if qs.count() != 0:
# choose ONE of the next two lines
self.is_the_chosen_one = False # keep the existing "chosen one"
#qs.update(is_the_chosen_one=False) # make this obj "the chosen one"
super(Character, self).save(*args, **kwargs)
class CharacterForm(forms.ModelForm):
class Meta:
model = Character
# if you want to use the new obj as the chosen one and remove others, then
# be sure to use the second line in the model save() above and DO NOT USE
# the following clean method
def clean_is_the_chosen_one(self):
chosen = self.cleaned_data.get('is_the_chosen_one')
if chosen:
qs = Character.objects.filter(is_the_chosen_one=True)
if self.instance.pk:
qs = qs.exclude(pk=self.instance.pk)
if qs.count() != 0:
raise forms.ValidationError("A Chosen One already exists! You will pay for your insolence!")
return chosen
Run Code Online (Sandbox Code Playgroud)
你也可以使用上面的表格来管理,只需使用
class CharacterAdmin(admin.ModelAdmin):
form = CharacterForm
admin.site.register(Character, CharacterAdmin)
Run Code Online (Sandbox Code Playgroud)
小智 6
就这样。
def save(self, *args, **kwargs):
if self.default_dp:
DownloadPageOrder.objects.all().update(**{'default_dp': False})
super(DownloadPageOrder, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
小智 5
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
def clean(self):
from django.core.exceptions import ValidationError
c = Character.objects.filter(is_the_chosen_one__exact=True)
if c and self.is_the_chosen:
raise ValidationError("The chosen one is already here! Too late")
Run Code Online (Sandbox Code Playgroud)
这样做可以在基本管理表单中进行验证
| 归档时间: |
|
| 查看次数: |
15735 次 |
| 最近记录: |