首页 > 解决方案 > Django 'ModelForm' 对象没有属性 'object'

问题描述

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

模型.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 上的内容:

表格.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__.

视图.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 上,我只是将表单保​​存在其模型上(带有刚刚添加的字段)。

第三种观点是我目前的问题。请记住,我可以使用流程做 3 件事:创建新流程、编辑流程或创建现有流程的新版本。这第三种观点打算做第三种观点。在获取时,我检查我是否正在尝试创建一个已经存在的进程。如果没有,我创建进程并将基本进程上的所有有用数据复制到它,除了 ManyToMany 字段(第二个“记住这一行”),因为我无法找到不保存的方法首先是模型。在发布时,我正在检查表单是否有效,在 form_valid 上,我正在保存该过程。

什么有效

我可以很好地创建和编辑流程。编辑时的只读字段完美运行。模型按预期保存。此外,如果注释了第一个“记住这一行”,也就是说,如果所有字段都是可编辑的,我可以使用另一个现有流程作为基础 (NewVersionProcessView) 创建一个新流程。

什么不起作用

如果第一个“记住这一行”没有注释,也就是说,如果某些字段是只读的,我的视图在发布时会崩溃。最具体地说,在 form_invalid 上。我唯一能想到的是,不知何故,没有一个 Process 对象链接到我的 NewVersionProcessView。我不明白为什么,因为我总是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。任何帮助表示赞赏。

标签: pythondjangodjango-formsdjango-views

解决方案


推荐阅读