【问题标题】:How to get Primary Key and data from Django SessionWizardView如何从 Django SessionWizardView 获取主键和数据
【发布时间】:2019-05-24 14:39:24
【问题描述】:

我有一个SessionWizardView,它可以生成 3 个表单,具体取决于之前的选择。选择表单后,用户填写表单并使用 POST 将其提交给每个案例的特定ListView。问题是获取这些值,主要是主键。

SessionWizardView,我遵循了文档 (https://django-formtools.readthedocs.io/en/stable/wizard.html#wizard-template-for-each-form),它按我预期的方式工作。 3 个表格的最后一个字段已预先填写,具体取决于第一个字段。

ListView 中,我尝试使用request.session.pop()SessionWizardView 中获取字段。在这里我有我的第一个疑问:我使用了forms.py 中的变量名(例如:request.session.pop('formField1', {}))和 POST 中的字段 id(request.session.pop('opcao1-formField1', {}))。在这两种情况下,浏览器都会返回'int' object has no attribute 'pk' 错误。

我已经测试了向导可以显示给我的 3 种表单,它们都显示相同的错误,所以我将放第一个的代码,所以文本保持简短。

urls.py:

logger = logging.getLogger(__name__)

urlpatterns = [
    # path('', views.IndexView.as_view(), name="index"),
    path('', views.IndexView.as_view(views.FORMS), name="index"),
    path('detail/', views.DetailView.as_view(), name="detail"),

views.py:

class IndexView(SessionWizardView):
    condition_dict = {"opcao1" :  graficoEscolhido1,
    "opcao2" :  graficoEscolhido2,
    "opcao3" :  graficoEscolhido3,
    }
    template_name = 'solid/index.html'
    success_url = reverse_lazy('detail')


    def done(self, form_list, **kwargs):
        self.request.session['opcao1-dateDataInicial'] = str(form_list.cleaned_data['opcao1-dateDataInicial'])
        self.request.session['opcao1-dateDataFinal'] = str(form_list.cleaned_data['opcao1-dateDataFinal'])
        self.request.session['opcao1-codigoDuto'] = form_list.cleaned_data['opcao1-codigoDuto']
        self.request.session['opcao1-codigoExtremidade'] = float(form_list.cleaned_data['opcao1-codigoExtremidade'])
        # I've already commented the previous line, trade wizard for 
        # request. Put form_list.get_all_cleaned_data(), too as test.
        return render(self.request, "solid/detail.html", {
            'form_data': [form.cleaned_data for form in form_list],
        })

class DetailView(generic.ListView):

    def get_context_data(self, **kwargs):
        cursor = connection.cursor()

        # Trying to get from previous session. I've replaced request for 
        # wizard too, in previous attempt.
        inicio = self.request.session.pop('opcao1-dateDataInicial', {})
        final = self.request.session.pop('opcao1-dateDataFinal', {})
        cod_id = self.request.session.pop('opcao1-codigoID', {})
        extra = self.request.session.pop('opcao1-valorExtra', {})

        Temp1 = TEMP.objects.values_list('TE_CD_ID_S_Temp', flat = True).filter(
            TE_CD_ID_Cod = cod_id,
            TE_VL_Ponto = extra)

        Dens1 = DENS.objects.values_list('DE_CD_ID_S_Den', flat = True).filter(
            DE_CD_ID_Cod = cod_id,
            DE_VL_Ponto = extra)

        Vaz1 = VAZ.objects.values_list('VA_CD_ID_S_Vaz', flat = True).filter(
            VA_CD_ID_Cod = cod_id,
            VA_VL_Ponto = extra)

        Pres1 = PRES.objects.values_list('PR_CD_ID_S_Pres', flat = True).filter(
            PR_CD_ID_Cod = cod_id,
            PR_VL_Ponto = extra)        

        cursor.execute('giant query',(str(sensorPres[0]), inicio, final, 
                        str(sensorTemp[0]), inicio, final, 
                        str(sensorDens[0]), inicio, final, 
                        str(sensorVaz[0]), inicio, final))

        lista = cursor.fetchall()
        listaArray = np.asarray(lista)
        tempo = listaArray[:,0]

        valpres = listaArray[:,1]
        valpres = np.array(valpres)
        valpres = valpres.astype(float)

        valtemp = listaArray[:,2]
        valtemp = np.array(valtemp)
        valtemp = valtemp.astype(float)

        valden = listaArray[:,3]
        valden = np.array(valden)
        valden = valden.astype(float)

        valvaz = listaArray[:,4]
        valvaz = np.array(valvaz)
        valvaz = valvaz.astype(float)

        kwargs['tempo'] = tempo
        kwargs['valpres'] = valpres
        kwargs['valtemp'] = valtemp
        kwargs['valden'] = valden
        kwargs['valvaz'] = valvaz
        kwargs['dateInicial'] = inicio
        kwargs['dateFinal'] = final

        return super().get_context_data(**kwargs)

forms.py:

class get_Codigo_DutoGrafico1(forms.Form):
# codigoID is a PK in the model.
    codigoID = forms.ModelMultipleChoiceField(label = "Código ID",        
        queryset = PRINCIPAL.objects.values_list('CD_ID', flat = True),
        widget = forms.SelectMultiple(attrs = {'id' : 'id_codigo_id'}))
    dateDataInicial = forms.DateTimeField(label = 'Data Inicial', 
        input_formats = ['%d/%m/%Y %H:%M:%S'], 
        widget=XDSoftDateTimePickerInput())
    dateDataFinal = forms.DateTimeField(label = 'Data Final',
        input_formats = ['%d/%m/%Y %H:%M:%S'],
        widget=XDSoftDateTimePickerInput())
    valorExtra = forms.ModelMultipleChoiceField(label = 'Valor Extra',
        queryset = DENS.objects.values_list('DE_VL_Ponto', flat = True).none(),
        widget = forms.SelectMultiple(attrs = {'id' : 'id_codigoDropDown'}))

index.html:

<div class = "container-fluid" id = "wrapper">
    <div class = "row">
        {% load i18n %}
        {% block head %}
        {{ wizard.form.media }}
        {% endblock %}
      {% block content %}
      <p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
      <form action = "" method = "post" id = "formGrafico"  data-data-url="{% url 'ajax_load_duto' %}" data-data2-url="{% url 'ajax_load_data2' %}" novalidate>
        {% csrf_token %}
      <table>
      {{ wizard.management_form }}
      {% if wizard.form.forms %}
          {{ wizard.form.management_form }}
          {% for form in wizard.form.forms %}
              {{ form }}
          {% endfor %}
      {% else %}
          {{ wizard.form }}
      {% endif %}
      </table>
      {% if wizard.steps.prev %}
        <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
        <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
      {% endif %}
      <input type="submit" value="{% trans "submit" %}"/>
      </form>
      {% endblock %}    

我希望获取 detail.html 中的值并以不同的方式显示数据。但是,我收到以下错误:“int”对象没有属性“pk”。 POST 返回以下内容:

POST
Variable    Value
csrfmiddlewaretoken 
'TArU7O0laFVnRiWp9rEpUOE4bUoPNYulB2zNkFbwnLFxbVBwr4Qmp7ZQ5FfhJ3r7'
index_view-current_step 
'opcao1'
opcao1-codigoDuto   
'177'
opcao1-dateDataInicial  
'03/05/2018 10:19:00'
opcao1-dateDataFinal    
'01/07/2018 10:20:00'
opcao1-codigoExtremidade    
'2,127000000000000' 

整个错误回溯:

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/

Django Version: 2.1.7
Python Version: 3.6.7
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.sites',
 'compressor',
 'django_extensions',
 'solid',
 'sekizai',
 'formtools']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  126.                 response = self.process_exception_by_middleware(e, request)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  124.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/formtools/wizard/views.py" in dispatch
  248.         response = super(WizardView, self).dispatch(request, *args, **kwargs)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/formtools/wizard/views.py" in post
  301.         if form.is_valid():

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/forms.py" in is_valid
  185.         return self.is_bound and not self.errors

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/forms.py" in errors
  180.             self.full_clean()

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/forms.py" in full_clean
  381.         self._clean_fields()

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/forms.py" in _clean_fields
  399.                     value = field.clean(value)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/models.py" in clean
  1293.         qs = self._check_values(value)

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/models.py" in _check_values
  1326.         pks = {str(getattr(o, key)) for o in qs}

File "/home/luiz/Projeto/Solid/venv/lib/python3.6/site-packages/django/forms/models.py" in <setcomp>
  1326.         pks = {str(getattr(o, key)) for o in qs}

Exception Type: AttributeError at /
Exception Value: 'int' object has no attribute 'pk'

所以数据是从 SessionWizardView 传过来的,但是中间有问题。为了测试“int”问题,我将 str() 设置为“cod_id”,但仍然收到“int”错误消息。我在 Stack 中搜索了与之相关的内容,但一无所获。

【问题讨论】:

  • 我不明白。当您发布表单时,它由您的IndexView 处理并运行done() 方法。你有return render(),它应该采用request、一个模板和一个上下文。不知道为什么您没有收到模板未找到错误,因为您在这里传递了 self.success_url 而不是模板名称。你在哪里打电话给你的ListView(你需要redirect到那个视图)
  • 请注意,done() 之后的WizardView 将删除会话数据。因此,如果您想在重定向到的下一个视图中使用它,您应该将提交的表单中的数据保存到数据库(或会话)中。
  • @dirkgroten 发布后,我将form_list.cleaned_data 传递给request.session,将html 链接放在render() 中,仍然得到同样的错误。我将通过这些更改更新问题。
  • 当您有错误跟踪时,请显示整个错误跟踪,而不仅仅是错误摘要。目前尚不清楚此错误发生在代码的哪一行。
  • @dirkgroten 抱歉,刚刚更新了整个错误跟踪。问题是错误不在我专门写的某行中。它是内部 django/python,所以我不知道如何处理它。就像我最后说的那样,我收到 str() 之间的 PK 字段,但仍然得到 int 错误。提前致谢。

标签: django django-formwizard django-formtools


【解决方案1】:

错误是因为您的ModelMultipleChoiceFieldqueryset 属性设置为整数列表,这是错误的,您的查询集应该是对象列表:PRINCIPAL.objects.all()ModelMultipleChoiceField 接收一个整数作为输入(发布的数据),但想要一个对象 (PRINCIPAL) 作为value

如果您想自定义值如何显示给用户,您应该继承ModelMultipleChoiceField 并覆盖label_from_instance 方法:

class IDModelMultipleChoiceField(ModelMultipleChoiceField):
    def label_from_instance(self, instance):
        return instance.pk

在你的表格中

cogidoID = IDModelMultipleChoiceField(queryset=..., label=...)

【讨论】:

  • 但是我需要我的表单只显示这个字段,所以我把 values_list 放到我想要的列中。如何克服?
  • 默认情况下,ModelMultipleChoiceField 将模型的__str__() 方法的结果显示为显示值(用户看到的值)。你的意思是你想实际向用户显示id?然后继承 ModelMultipleChoiceField 并覆盖 label_from_instance() 方法以仅返回 id
  • 我已经使用你的建议把object.all() 和错误消失了,现在我的问题是验证valorExtra。我做了一个clean_valorExtra() 将它设置为浮动,但它不起作用。我认为这是因为它在 models.py 中设置为 DECIMAL。我将尝试在 python 中设置为十进制。
  • 我已更改为ChoiceField 并使用self.data[]_init_ 中获得价值。它有效,但是您的解决方案跳过了第一个问题并帮助我找出了问题。我会把它当作解决的,谢谢=)
猜你喜欢
  • 1970-01-01
  • 2014-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-27
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
相关资源
最近更新 更多