Tôi gặp rất nhiều khó khăn trong việc tìm ra cách tự động tạo một thể hiện của mô hình cho trường ForeignKey khi một biểu mẫu được gửi. Đây là một trang web đồ chơi đơn giản minh họa sự cố:Django: ValueError: không thể gán không có gì trên ForeignField khi cố gắng tạo ra trong sạch thất bại
Tôi có hai mô hình, Model1 và Model2. Model2 chứa ForeignKey đến Model1. Tôi muốn người dùng có thể tạo một thể hiện của Model2 bằng cách chọn cụ thể một thể hiện của Model1 để lưu trữ trong ForeignKey, hoặc bằng cách để trống giá trị đó và cho phép một cá thể của Model1 được tạo tự động.
Đây là những gì tôi cảm thấy giống như mã đó sẽ như thế nào. Mã models.py của tôi rất đơn giản:
# models.py
from django.db import models
from django.core.validators import MinValueValidator
class Model1(models.Model):
# Note this field cannot be negative
my_field1 = models.IntegerField(validators=[MinValueValidator(0)])
class Model2(models.Model):
# blank = True will make key_to_model1 not required on the form,
# but since null = False, I will still require the ForeignKey
# to be set in the database.
related_model1 = models.ForeignKey(Model1, blank=True)
# Note this field cannot be negative
my_field2 = models.IntegerField(validators=[MinValueValidator(0)])
forms.py là một chút liên quan, nhưng những gì đang xảy ra khá đơn giản. Nếu Model2Form không nhận được một thể hiện của Model1, nó sẽ cố gắng tự động tạo một thể hiện trong phương thức sạch, xác nhận nó, và nếu nó hợp lệ, nó sẽ lưu nó. Nếu nó không hợp lệ, nó sẽ đưa ra một ngoại lệ.
#forms.py
from django import forms
from django.forms.models import model_to_dict
from .models import Model1, Model2
# A ModelForm used for validation purposes only.
class Model1Form(forms.ModelForm):
class Meta:
model = Model1
class Model2Form(forms.ModelForm):
class Meta:
model = Model2
def clean(self):
cleaned_data = super(Model2Form, self).clean()
if not cleaned_data.get('related_model1', None):
# Don't instantiate field2 if it doesn't exist.
val = cleaned_data.get('my_field2', None)
if not val:
raise forms.ValidationError("My field must exist")
# Generate a new instance of Model1 based on Model2's data
new_model1 = Model1(my_field1=val)
# validate the Model1 instance with a form form
validation_form_data = model_to_dict(new_model1)
validation_form = Model1Form(validation_form_data)
if not validation_form.is_valid():
raise forms.ValidationError("Could not create a proper instance of Model1.")
# set the model1 instance to the related model and save it to the database.
new_model1.save()
cleaned_data['related_model1'] = new_model1
return cleaned_data
Tuy nhiên, cách tiếp cận này không hoạt động. Nếu tôi nhập dữ liệu hợp lệ vào biểu mẫu của tôi, nó hoạt động tốt. Nhưng, nếu tôi không nhập bất cứ điều gì cho ForeignKey và đặt một giá trị âm cho số nguyên, tôi nhận được một ValueError.
Traceback: File "/Library/Python/2.7/site-packages/django/core/handlers/base.py" in get_response 111. response = callback(request, *callback_args, **callback_kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/base.py" in view 48. return self.dispatch(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/base.py" in dispatch 69. return handler(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/edit.py" in post 172. return super(BaseCreateView, self).post(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/edit.py" in post 137. if form.is_valid(): File "/Library/Python/2.7/site-packages/django/forms/forms.py" in is_valid 124. return self.is_bound and not bool(self.errors) File "/Library/Python/2.7/site-packages/django/forms/forms.py" in _get_errors 115. self.full_clean() File "/Library/Python/2.7/site-packages/django/forms/forms.py" in full_clean 272. self._post_clean() File "/Library/Python/2.7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) File "/Library/Python/2.7/site-packages/django/forms/models.py" in construct_instance 51. f.save_form_data(instance, cleaned_data[f.name]) File "/Library/Python/2.7/site-packages/django/db/models/fields/init.py" in save_form_data 454. setattr(instance, self.name, data) File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py" in set 362. (instance._meta.object_name, self.field.name))
Exception Type: ValueError at /add/ Exception Value: Cannot assign None: "Model2.related_model1" does not allow null values.
Vì vậy, những gì đang xảy ra là Django đang bắt ValidationError của tôi và vẫn tạo ra một thể hiện của Model2 mặc dù xác thực không thành công.
Tôi có thể sửa lỗi này bằng cách ghi đè phương thức _post_clean để không tạo phiên bản Model2 nếu có lỗi. Nhưng, giải pháp đó là xấu. Đặc biệt, hành vi của _post_clean rất hữu ích nói chung - Trong các dự án phức tạp hơn, tôi cần _post_clean để chạy vì các lý do khác.
Tôi cũng có thể cho phép ForeignKey rỗng nhưng không bao giờ được đặt thành null trong thực tế. Nhưng, một lần nữa, điều đó có vẻ như là một ý tưởng tồi.
Tôi thậm chí có thể thiết lập một mô hình giả1 mà tôi sử dụng bất cứ khi nào xác nhận trên mẫu mới đã thất bại Model1, nhưng điều đó cũng có vẻ bị hack.
Nói chung, tôi có thể nghĩ ra rất nhiều hacks để sửa lỗi này, nhưng tôi không biết cách sửa lỗi này theo cách hợp lý, gọn gàng.
Tắt đầu của tôi Tôi nghĩ bạn có thể cần ít nhất trao đổi lưu trữ và xóa sạch_data xung quanh (như trong model1.save sau đó related_model1 = model1) –
Xin chào Jeff, Cảm ơn bạn đã trả lời nhanh! Tôi nghĩ rằng thứ tự đó không quan trọng, vì các biến này là các tham chiếu và mô hình đã được xác nhận, do đó, lưu sẽ không bao giờ thất bại. Đơn đặt hàng của bạn dường như tự nhiên hơn, mặc dù, vì vậy tôi sẽ chuyển đổi nó. Tuy nhiên, mã này không bao giờ được tiếp cận khi lỗi đang xảy ra, do đó, đó không phải là nguyên nhân của lỗi. –
thêm dòng sau 'nhập pdb; pdb.set_trace() 'để chỉ trước khi tạo new_model1 và sau đó bạn có thể chạy một dòng tại một thời điểm (bằng cách sử dụng n) và các giá trị của new_model1 đến thiết bị đầu cuối của bạn đang chạy django. Tôi nghi ngờ một cái gì đó là đi một chút sai lầm về việc tạo validation_form và kiểm tra nếu nó là hợp lệ. [link to docs on pdb] (http://docs.python.org/2/library/pdb.html) –