Django'ModelForm'对象没有属性'object'

时间:2018-08-07 21:45:41

标签: python django django-forms django-views

我有一个ModelForm,我试图在禁用某些字段的情况下显示它。在这种情况下,process_id。 我的模型的重要部分如下所示:

models.py

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上拥有的东西:

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__上的注释行。

views.py

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。 任何帮助表示赞赏。

0 个答案:

没有答案