Django의 고유 한 BooleanField 값?
내 models.py가 다음과 같다고 가정합니다.
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
내 Character
인스턴스 중 하나만 갖고 is_the_chosen_one == True
다른 모든 인스턴스를 갖기를 원합니다 is_the_chosen_one == False
. 이 고유성 제약이 존중되는지 어떻게 가장 잘 확인할 수 있습니까?
데이터베이스, 모델 및 (관리자) 양식 수준에서 제약 조건 준수의 중요성을 고려한 답변에 대한 최고 점수!
이 작업을 수행해야 할 때마다 모델에 대한 저장 방법을 재정의하고 다른 모델에 이미 플래그가 설정되어 있는지 확인하고 해제하도록했습니다.
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)
모델의 저장 방법을 재정의하고 부울을 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)
Adam의 비슷한 답변을 편집하려고 시도했지만 원래 답변을 너무 많이 변경하여 거부되었습니다. 이 방법은 다른 항목의 검사가 단일 쿼리로 수행되기 때문에 더 간결하고 효율적입니다.
사용자 지정 모델 정리 / 저장을 사용하는 대신 .NET에서 메서드를 재정의 하는 사용자 지정 필드를 만들었습니다 . 대신 다른 필드가 있다면 오류를 제기의 , 나는 다른 모든 필드를 만든 이 있다면 . 또한 필드가 있고 다른 필드가 없으면 오류를 발생시키는 대신 필드를 다음 과 같이 저장했습니다.pre_save
django.db.models.BooleanField
True
False
True
False
True
True
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"])
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)
다음 솔루션은 약간 추악하지만 작동 할 수 있습니다.
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)
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)
@Ellis Percival : 데이터베이스에 한 번만 추가로 히트하고 현재 항목을 선택한 항목으로 허용합니다. 깨끗하고 우아합니다.
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)
내 경우에 적합하지 않지만 실행 가능한 다른 솔루션 :
@nemocorp 는 clean
유효성 검사를 수행하는 메서드를 재정의합니다 . 그러나 어떤 모델이 "하나"인지는보고하지 않으며 사용자 친화적이지 않습니다. 그럼에도 불구하고 특히 누군가 @Flyte만큼 공격적이지 않으려는 경우 매우 좋은 접근 방식입니다.
@ saul.shanabrook 및 @Thierry J. 는 다른 "is_the_one"항목을 변경 False
하거나 ValidationError
. 나는 그것이 절대적으로 필요하지 않는 한 내 Django 설치에 새로운 기능을 적용하는 것을 꺼려합니다.
@daigorocub: Uses Django signals. I find it a unique approach and gives a hint of how to use Django Signals. However I am not sure whether this is a -strictly speaking- "proper" use of signals since I cannot consider this procedure as part of a "decoupled application".
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
You can use the above form for admin as well, just use
class CharacterAdmin(admin.ModelAdmin):
form = CharacterForm
admin.site.register(Character, CharacterAdmin)
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")
Doing this made the validation available in the basic admin form
And that's all.
def save(self, *args, **kwargs):
if self.default_dp:
DownloadPageOrder.objects.all().update(**{'default_dp': False})
super(DownloadPageOrder, self).save(*args, **kwargs)
Using a similar approach as Saul, but slightly different purpose:
class TrueUniqueBooleanField(BooleanField):
def __init__(self, unique_for=None, *args, **kwargs):
self.unique_for = unique_for
super(BooleanField, self).__init__(*args, **kwargs)
def pre_save(self, model_instance, add):
value = super(TrueUniqueBooleanField, self).pre_save(model_instance, add)
objects = model_instance.__class__.objects
if self.unique_for:
objects = objects.filter(**{self.unique_for: getattr(model_instance, self.unique_for)})
if value and objects.exclude(id=model_instance.id).filter(**{self.attname: True}):
msg = 'Only one instance of {} can have its field {} set to True'.format(model_instance.__class__, self.attname)
if self.unique_for:
msg += ' for each different {}'.format(self.unique_for)
raise ValidationError(msg)
return value
This implementation will raise a ValidationError
when attempting to save another record with a value of True.
Also, I have added the unique_for
argument which can be set to any other field in the model, to check true-uniqueness only for records with the same value, such as:
class Phone(models.Model):
user = models.ForeignKey(User)
main = TrueUniqueBooleanField(unique_for='user', default=False)
Do I get points for answering my question?
problem was it was finding itself in the loop, fixed by:
# is this the testimonial image, if so, unselect other images
if self.testimonial_image is True:
others = Photograph.objects.filter(project=self.project).filter(testimonial_image=True)
pdb.set_trace()
for o in others:
if o != self: ### important line
o.testimonial_image = False
o.save()
I tried some of these solutions, and ended up with another one, just for the sake of code shortness (don't have to override forms or save method). For this to work, the field can't be unique in it's definition but the signal makes sure that happens.
# making default_number True unique
@receiver(post_save, sender=Character)
def unique_is_the_chosen_one(sender, instance, **kwargs):
if instance.is_the_chosen_one:
Character.objects.all().exclude(pk=instance.pk).update(is_the_chosen_one=False)
참고URL : https://stackoverflow.com/questions/1455126/unique-booleanfield-value-in-django
'Program Tip' 카테고리의 다른 글
비교적 큰 Flask 애플리케이션을 구성하는 방법은 무엇입니까? (0) | 2020.10.21 |
---|---|
MySQL 데이터베이스에 연결하는 방법은 무엇입니까? (0) | 2020.10.21 |
Cast 또는 Convert를 사용하는 경우 (0) | 2020.10.21 |
PostgreSQL 9.2.1과 Hibernate 연결 (0) | 2020.10.21 |
디버그 중 SSIS에서 변수 감시 (0) | 2020.10.21 |