我有一个ModelForm,我试图在禁用某些字段的情况下显示它。在这种情况下,process_id
。
我的模型的重要部分如下所示:
class Process(models.Model):
related_processes = models.ManyToManyField('self', blank=True, symmetrical=False)
process_id = models.CharField(max_length=20, primary_key=True)
normal_field = models.CharField(max_length=20)
# a lot of fields here...
所以基本上我有一个可以包含零个或多个相关进程的进程。 这就是我在forms.py上拥有的东西:
class ProcessForm(ModelForm):
class Meta:
model = Process
fields = '__all__'
class EditProcessForm(ProcessForm):
readonly_fields = ('process_id', )
def __init__(self, *args, **kwargs):
super(EditProcessForm, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.items() if name in self.readonly_fields):
field.widget.attrs['disabled'] = 'true'
field.required = False
def clean(self):
for f in self.readonly_fields:
self.cleaned_data.pop(f, None)
return super(EditProcessForm, self).clean()
class NewVersionProcessForm(EditProcessForm):
readonly_fields = ('process_id', )
def __init__(self, *args, **kwargs):
super(NewVersionProcessForm, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.items() if name in self.readonly_fields):
# field.widget.attrs['disabled'] = 'true'
# Remember this line ^
field.required = False
客户端第一次填写表单时,我希望所有字段都是可编辑的,因此我使用ProcessForm。但是当客户端编辑流程时,我希望某些字段为只读。我在stackoverflow上找到了这个不错的解决方案(不幸的是,我再也找不到了),并且在编辑流程时效果很好。
EditProcessForm和NewVersionProcessForm之间的区别在于它们的视图和__init__
上的注释行。
class ProcessFormView(FormView):
template_name = 'my_app/fill_form.html'
form_class = ProcessForm
def form_valid(self, form):
form.save()
return redirect('my_app:show_process_page', form.cleaned_data.get('process_id'))
class EditProcessView(UpdateView):
model = Process
form_class = EditProcessForm
template_name = 'my_app/edit_form.html'
pk_url_kwarg = 'process_id'
def post(self, request, process_id):
# This is a little hack I found here: https://stackoverflow.com/a/21262262/3773461
# to edit an immutable QueryDict.
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['process_id'] = process_id
request.POST._mutable = mutable
return super().post(request)
def form_valid(self, form):
form.save()
return redirect('my_app:show_process_page', self.kwargs['process_id'])
class NewVersionProcessView(EditProcessView):
template_name = 'my_app/new_version_form.html'
form_class = NewVersionProcessForm
def get(self, request, process_id):
try:
Process.objects.get(process_id=process_id)
return redirect('my_app:process_already_exists_page')
except Process.DoesNotExist:
self.object = Process(process_id=process_id)
last_id = self.object.get_last_version_id()
last_process = Process.objects.get(process_id=last_id)
last_process_dict = last_process.__dict__
related_processes = last_process.related_processes.all()[:]
exclude = ['_state', 'process_id']
for key, value in last_process_dict.items():
if key not in exclude:
self.object.__dict__[key] = value
# self.object.related_processes.add(*related_processes)
# Remember this line too ^
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.form_invalid(form)
def post(self, request, process_id):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return redirect('my_app:show_process_page', form.cleaned_data['process_id'])
第一个视图是带有ProcessForm的简单FormView。第二个视图是UpdateView。在发布后,我只添加表单上已禁用的字段(因此通常不会向前传递)。在form_valid上,我只是将表单保存在其模型中(带有刚刚添加的字段)。
第三个观点是我当前的问题。请记住,对于一个流程,我可以做三件事:创建一个新流程,对其进行编辑,或者为现有流程创建一个新版本。第三种观点打算做第三种观点。 一经发现,我会检查是否尝试创建一个已经存在的进程。如果没有,我将创建该流程并将基本流程上的所有有用数据复制到该流程上,但ManyToMany字段除外(第二个“记住此行”),因为我想不出一种方法来保存它模特第一。 发布后,我正在检查表单是否有效,而在form_valid上,我正在保存过程。
我可以创建和编辑流程。编辑时的只读字段可以正常工作。模型将按预期保存。 另外,如果注释了第一个“记住此行”,也就是说,如果所有字段都是可编辑的,则可以使用另一个现有过程作为基础(NewVersionProcessView)创建一个新过程。
如果未注释第一个“记住此行”,即,如果某些字段是只读的,则视图在发布时崩溃。最具体地说,在form_invalid上。我唯一能想到的是,某种程度上没有一个链接到我的NewVersionProcessView的Process对象。我不明白为什么会这样,因为我总是在获取时为self.object
分配一些东西。我无法从数据库中获取对象,因为它尚未保存。另外,我无法理解为什么启用或禁用字段会影响self.object
。请记住,唯一改变的是那行field.widget.attrs['disabled'] = 'true'
。
这是日志:
Request Method: POST
Request URL: http://127.0.0.1:8000/my_app/new_version/999-11/
Django Version: 2.0.7
Python Version: 3.4.4
Installed Applications:
['my_app.apps.MyAppConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "C:\Python34\lib\site-packages\django\core\handlers\exception.py" in inner
35. response = get_response(request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py" in _get_response
128. response = self.process_exception_by_middleware(e, request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py" in _get_response
126. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python34\lib\site-packages\django\views\generic\base.py" in view
69. return self.dispatch(request, *args, **kwargs)
File "C:\Python34\lib\site-packages\django\views\generic\base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "C:\Users\***\Desktop\my_app\views.py" in post
124. return self.form_invalid(form)
File "C:\Python34\lib\site-packages\django\views\generic\edit.py" in form_invalid
61. return self.render_to_response(self.get_context_data(form=form))
File "C:\Python34\lib\site-packages\django\views\generic\edit.py" in get_context_data
67. return super().get_context_data(**kwargs)
File "C:\Python34\lib\site-packages\django\views\generic\detail.py" in get_context_data
93. if self.object:
Exception Type: AttributeError at /my_app/new_version/999-11/
Exception Value: 'NewVersionProcessView' object has no attribute 'object'
如果需要,我可以提供urls.py和htmls。 任何帮助表示赞赏。