Django ' s ModelForm unique
Mam Model Django, który wygląda tak.
class Solution(models.Model):
'''
Represents a solution to a specific problem.
'''
name = models.CharField(max_length=50)
problem = models.ForeignKey(Problem)
description = models.TextField(blank=True)
date = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ("name", "problem")
Używam formularza do dodawania modeli, które wyglądają tak:
class SolutionForm(forms.ModelForm):
class Meta:
model = Solution
exclude = ['problem']
Mój problem polega na tym, że SolutionForm
nie sprawdza ograniczenia Solution
i dlatego zwraca IntegrityError
podczas próby zapisania formularza. Wiem, że mógłbym użyć validate_unique
, aby ręcznie To sprawdzić, ale zastanawiałem się, czy jest jakiś sposób, aby złapać to w walidacji formularza i automatycznie zwrócić błąd formularza.
9 answers
Rozwiązałem ten sam problem poprzez nadpisanie metody validate_unique()
Modelu:
def validate_unique(self):
exclude = self._get_validation_exclusions()
exclude.remove('problem') # allow checking against the missing attribute
try:
self.instance.validate_unique(exclude=exclude)
except ValidationError, e:
self._update_errors(e.message_dict)
Teraz po prostu zawsze upewniam się, że atrybut Nie podany w formularzu jest nadal dostępny, np. instance=Solution(problem=some_problem)
na inicjalizatorze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-21 06:40:03
Jak mówi Felix, Modelformy powinny sprawdzać ograniczenie unique_together
w ich walidacji.
Jednak w Twoim przypadku faktycznie wykluczasz jeden element tego ograniczenia ze swojego formularza. Domyślam się, że to jest Twój problem - jak FORMULARZ ma sprawdzić ograniczenie, jeśli połowa z nich nie jest nawet na formularzu?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-26 20:19:46
Udało mi się to naprawić bez modyfikowania widoku, dodając czystą metodę do formularza:
class SolutionForm(forms.ModelForm):
class Meta:
model = Solution
exclude = ['problem']
def clean(self):
cleaned_data = self.cleaned_data
try:
Solution.objects.get(name=cleaned_data['name'], problem=self.problem)
except Solution.DoesNotExist:
pass
else:
raise ValidationError('Solution with this Name already exists for this problem')
# Always return cleaned_data
return cleaned_data
Jedyne co muszę teraz zrobić w widoku to dodać właściwość problem do formularza przed wykonaniem is_valid
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-06 11:30:23
Rozwiązanie z @sttwister jest słuszne, ale można je uprościć.
class SolutionForm(forms.ModelForm):
class Meta:
model = Solution
exclude = ['problem']
def clean(self):
cleaned_data = self.cleaned_data
if Solution.objects.filter(name=cleaned_data['name'],
problem=self.problem).exists():
raise ValidationError(
'Solution with this Name already exists for this problem')
# Always return cleaned_data
return cleaned_data
Jako bonus nie wycofujesz obiektu w przypadku jego duplikacji, a jedynie sprawdzasz, czy istnieje w bazie danych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-05 10:03:17
Z Pomocą odpowiedzi Jarmo, poniższe wydaje mi się działać ładnie (w Django 1.3), ale możliwe, że złamałem jakiś narożny przypadek (jest wiele biletów wokół _get_validation_exclusions
):
class SolutionForm(forms.ModelForm):
class Meta:
model = Solution
exclude = ['problem']
def _get_validation_exclusions(self):
exclude = super(SolutionForm, self)._get_validation_exclusions()
exclude.remove('problem')
return exclude
Nie jestem pewien, ale wydaje mi się, że to błąd Django... ale musiałbym rozejrzeć się po wcześniej zgłoszonych problemach.
Edit: za szybko się odezwałem. Może to, co napisałem powyżej, zadziała w niektórych sytuacjach, ale nie w moich; skończyło się na tym, że skorzystałem bezpośrednio z odpowiedzi Jarmo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-09 16:52:46
Będziesz musiał zrobić coś takiego:
def your_view(request):
if request.method == 'GET':
form = SolutionForm()
elif request.method == 'POST':
problem = ... # logic to find the problem instance
solution = Solution(problem=problem) # or solution.problem = problem
form = SolutionForm(request.POST, instance=solution)
# the form will validate because the problem has been provided on solution instance
if form.is_valid():
solution = form.save()
# redirect or return other response
# show the form
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-27 09:54:09
Jeśli chcesz, aby Komunikat o błędzie był powiązany z polem name
(i pojawiał się obok niego):
def clean(self):
cleaned_data = super().clean()
name_field = 'name'
name = cleaned_data.get(name_field)
if name:
if Solution.objects.filter(name=name, problem=self.problem).exists():
cleaned_data.pop(name_field) # is also done by add_error
self.add_error(name_field, _('There is already a solution with this name.'))
return cleaned_data
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-15 09:45:41
Moje rozwiązanie opiera się na Django 2.1
Zostaw SolutionForm w spokoju, użyj metody save () w rozwiązaniu
class Solution(models.Model):
...
def save(self, *args, **kwargs):
self.clean()
return super(Solution, self).save(*args, **kwargs)
def clean():
# have your custom model field checks here
# They can raise Validation Error
# Now this is the key to enforcing unique constraint
self.validate_unique()
Wywołanie full_clean() w save () nie działa, ponieważ ValidationError zostanie anulowane
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-26 20:57:33
W moim przypadku musiałem wykluczyć pole company
i dodać je do funkcji widoku form_valid
. Skończyło się na tym, że wykonałem następujące czynności (czerpiąc inspirację z różnych odpowiedzi).
W moim CreateView
def form_valid(self, form):
cleaned_data = form.cleaned_data
user_company = self.request.user.profile.company
if UnitCategory.objects.filter(code=cleaned_data['code'],
company=user_company).exists():
form.add_error('code', _(
'A UnitCategory with this Code already exists for this company.'))
return super(UnitCategoryCreateView, self).form_invalid(form)
if UnitCategory.objects.filter(color=cleaned_data['color'],
company=user_company).exists():
form.add_error('color', _(
'A UnitCategory with this Color already exists for this company.'))
return super(UnitCategoryCreateView, self).form_invalid(form)
form.instance.company = user_company
return super(UnitCategoryCreateView, self).form_valid(form)
W moim UpdateView
musiałem wykluczyć bieżącą instancję obiektu w sprawdzaniu, czy zapytanie istnieje za pomocą exclude(pk=self.kwargs['pk'])
def form_valid(self, form):
cleaned_data = form.cleaned_data
user_company = self.request.user.profile.company
if UnitCategory.objects.filter(code=cleaned_data['code'],
company=user_company).exclude(pk=self.kwargs['pk']).exists():
form.add_error(
'code', _('A UnitCategory with this Code already exists for this company.'))
return super(UnitCategoryUpdateView, self).form_invalid(form)
if UnitCategory.objects.filter(color=cleaned_data['color'],
company=user_company).exclude(pk=self.kwargs['pk']).exists():
form.add_error('color', _(
'A UnitCategory with this Color already exists for this company.'))
return super(UnitCategoryUpdateView, self).form_invalid(form)
# Return form_valid if no errors raised
# Add logged-in user's company as form's company field
form.instance.company = user_company
return super(UnitCategoryUpdateView, self).form_valid(form)
Nie najczystsze rozwiązanie, na które liczyłem, ale pomyślałem, że może komuś się przydać.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-06 00:00:53