Почему при просмотре уже отправленной формы запускается чистая модель?
У меня есть веб-сайт, который позволяет пользователям отправлять одну и ту же форму несколько раз (но с разными входными данными), но также позволяет им просматривать каждую отправленную форму и редактировать ее.
Проблема связана с полем "имя_ключа". Если я просматриваю уже отправленную форму (edit_keydef), то появляется сообщение ValidationError с полем, выделенным красным цветом, тогда как на самом деле я хотел, чтобы ValidationError возникала только тогда, когда они собираются отправить форму, но указали то же имя, что и в другой форме. (Представление, используемое при заполнении формы, отличается от представления при редактировании формы - так как при редактировании больше полей), но оба представления используют одну и ту же модель.
Я не понимаю, почему DJango запускает функцию clean(), когда форма уже заполнена, т.е. валидация уже произошла и данные уже отправлены
views.py
def edit_keydef(request, id):
data = {'title': 'View or Edit Key Definition'}
template = 'generate_keys/edit_keydef.html'
keydef = get_object_or_404(KeyDefinition, pk=id)
if request.method == "POST":
keydetails_form_instance = KeyDefDetailsForm(request.POST, instance=keydef)
if 'Save' in request.POST:
if keydetails_form_instance.is_valid():
if keydetails_form_instance.cleaned_data['encoded_activation_key']:
activation_obj = Activation()
result = activation_obj.GenerateKey(keydef)
keydetails_form_instance.save()
return redirect('/key/edit/' + str(id))
else:
log.debug(keydetails_form_instance.errors)
if 'Generate' in request.POST:
if keydetails_form_instance.is_valid():
activation_obj = Activation()
result = activation_obj.GenerateKey(keydef)
if "error" in result:
data['error_msg'] = format_html(
'<p>Failed to generate activation key because:</p>'
+ '<p>'
+ str(result['error'])
+ '</p>'
)
else:
keydetails_form_instance.save()
return redirect('/key/edit/' + str(id))
else:
log.debug(keydetails_form_instance.errors)
else:
keydetails_form_instance = None
if not id:
keydetails_form_instance = KeyDefDetailsForm()
else:
# Retrieve a previously saved form.
try:
keydetails = KeyDefinition.objects.get(pk=id)
keydetails_form_instance = KeyDefDetailsForm(keydetails.to_dict(),
instance = keydetails)
except KeyDefinition.DoesNotExist as e:
return redirect('/record_does_not_exist')
# Form instance has been saved at this stage so update the variant list.
data['form'] = keydetails_form_instance
data['set_product_options_url'] = reverse_lazy('set_product_options', kwargs={'id':id})
return render(request, template, data)
models.py
class KeyDefinition (models.Model) :
...
def clean (self) :
....
# ensure that each user can't have two keys with the same name
key_name_exists = KeyDefinition.objects.filter(key_name=self.key_name, developer_email=self.developer_email)
if key_name_exists:
raise ValidationError (
{'key_name' : ['This Key Name already exists']}
)
class Meta:
verbose_name = "Key Definition"
unique_together = [['key_name', 'developer_email']]
forms.py
class KeyDefDetailsForm (ModelForm) :
def __init__(self, *args, **kwargs) :
super(ModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.fields['version'].widget.attrs['class'] = "major_minor"
def update_variants(self, keydef_obj):
self.fields.update({
'feature_variant': forms.ModelChoiceField(
widget = Select(),
queryset = FeatureVariant.objects.filter(product__feature_name=keydef_obj.feature),
required = False,
label = "Product"
)
})
def update_available_hosts(self, keydef_obj):
# only show hosts that have been created by this user
self.fields.update({
'available_hosts': forms.ModelChoiceField(
empty_label=None,
initial = AvailableHosts.objects.get(host_label=keydef_obj.host_label).id,
widget=Select(),
queryset = AvailableHosts.objects.filter(host_developer_email=keydef_obj.developer_email),
required = False,
label = "Available Hosts"
)
})
class Meta :
model = KeyDefinition
fields = '__all__'
widgets = {
'customer_name' : TextInput(),
'host_method' : TextInput(),
'issue_date' : TextInput(attrs={'type':'date'}),
'expiry_date': TextInput(attrs={"type":"date"}),
'available_hosts' : Select(),
'country' : Select(),
'feature' : Select(),
'activation_request' : HiddenInput(),
'hostid_provision' : HiddenInput(),
}
Метод KeyDefinition
действительно существует: соответствует именно тот, который вы редактируете. Метод clean()
будет всегда выполняться при валидации формы. Поэтому вы должны исключить этот объект, поэтому с:
class KeyDefinition(models.Model):
def clean(self):
# …
# ensure that each user can't have two keys with the same name
key_name_exists = (
KeyDefinition.objects.filter(
key_name=self.key_name, developer_email=self.developer_email
)
.exclude(pk=self.pk)
.exists()
)
if key_name_exists:
raise ValidationError({'key_name': ['This Key Name already exists']})
return super().clean()
Таким образом, мы исключаем собственный объект с помощью .exclude(pk=self.pk)
.
Note: It is more efficient to use
.exists()
[Django-doc] than to check the truthiness of aQuerySet
, since this will not load the records from the queryset, and thus will minimize the bandwidth used between the database and the Django/Python layer.