Django:使用formtools在页面中拆分表单并将其保存到数据库

时间:2018-11-08 02:25:26

标签: django

我有一个包含3个部分的表单:

1)tamanios(大小),2)念珠菌(数量),3)archivos subidos(上传的图片)。

我需要在一页中分开1和2,在另一页中分开3,然后将表单数据保存到数据库中。

据我调查,可以使用formtools实现。但是,使用formtools,我不得不将我的模型分为2个模型:a)TamaniosCantidades,b)ArchivosSubidos,以便可以在不同的“步骤”中进行渲染。

尽管如此,我宁愿只使用1个模型。但是,如果该解决方案包含一些ForeingKey来连接两个拆分的模型,则可以。

将表单另存为数据库中的模型对象需要做什么?

我想不出如何在向导类中编写“完成”方法。

原始模型:

class TamaniosCantidades(models.Model):
    TAMANIOS = (('2x2', '2" x 2"',), ('3x3', '3" x 3"',),
               ('4x4', '4" x 4"',), ('5x5', '5" x 5"',))

    CANTIDADES = (('50', '50',), ('100', '100',),
                ('150', '150',))


    tamanios = models.CharField(max_length=10, choices=TAMANIOS)
    cantidades = models.CharField(max_length=10, choices=CANTIDADES)
    imagenes = models.FileField(upload_to='imagenes/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

修改后的模型(分解结果):

class TamaniosCantidades(models.Model):
    TAMANIOS = (('2x2', '2" x 2"',), ('3x3', '3" x 3"',),
               ('4x4', '4" x 4"',), ('5x5', '5" x 5"',))

    CANTIDADES = (('50', '50',), ('100', '100',),
                ('150', '150',))

    tamanios = models.CharField(max_length=10, choices=TAMANIOS)
    cantidades = models.CharField(max_length=10, choices=CANTIDADES)
    # imagenes = models.FileField(upload_to='imagenes/')
    # uploaded_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.tamanios


class ArchivosSubidos(models.Model):
    # imagenes = models.FileField(upload_to='imagenes/') #commented because I was having problems uploading files with formtools
    imagenes = models.CharField(max_length=100) #using charfield to test splitting
    uploaded_at = models.DateTimeField(auto_now_add=True)

views.py

class ContactWizard(SessionWizardView):
    template_name = "main_app/contact_form.html"
    file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'imagenes'))

    def done(self, form_list, **kwargs):
        form_data = process_form_data(form_list)

        return HttpResponseRedirect('/')
        # return HttpResponseRedirect('/done.html')
        # return render('main_app/done.html', {'form_data':form_data})


def process_form_data(form_list):
    form_data = [form.cleaned_data for form in form_list]

    return form_data

urls.py

app_name = 'main_app'
urlpatterns = [
    path('', views.index),
    path('productos/', views.productos),
    # path('productos/die-cut-stickers', views.die_cut, name='die-cut-stickers'),
    path('contact/', ContactWizard.as_view([TamaniosCantidadesForm, ArchivosSubidosForm]))
]

更新1:堆栈跟踪

2x2 #printed from print(kwargs.get('tamanios', None)) in  def save
Internal Server Error: /step-three/
Traceback (most recent call last):
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 296, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: NOT NULL constraint failed: main_app_tamanioscantidades.tamanios

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/views/generic/base.py", line 88, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/views/generic/edit.py", line 172, in post
    return super().post(request, *args, **kwargs)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/views/generic/edit.py", line 142, in post
    return self.form_valid(form)
  File "/home/ogonzales/Escritorio/web_proyects/gallito/main_app/views.py", line 195, in form_valid
    return super(StepThreeView, self).form_valid(form)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/views/generic/edit.py", line 125, in form_valid
    self.object = form.save()
  File "/home/ogonzales/Escritorio/web_proyects/gallito/main_app/forms.py", line 55, in save
    instance.save()
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/base.py", line 718, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/base.py", line 748, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/base.py", line 812, in _save_table
    forced_update)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/base.py", line 861, in _do_update
    return filtered._update(values) > 0
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/query.py", line 712, in _update
    return query.get_compiler(self.db).execute_sql(CURSOR)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1383, in execute_sql
    cursor = super().execute_sql(result_type)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1065, in execute_sql
    cursor.execute(sql, params)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/ogonzales/Escritorio/projects_envs/gallito_env/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 296, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: NOT NULL constraint failed: main_app_tamanioscantidades.tamanios
[17/Nov/2018 03:53:44] "POST /step-three/ HTTP/1.1" 500 178292

1 个答案:

答案 0 :(得分:2)

使用formtools可能不是一个不错的选择(恕我直言:IMHO:对您的表单的控制较少),相反,您可以将Django FormModelform用于多步表单,并在每个步骤中使用django session存储数据。最后,您可以弹出这些会话数据并将其存储在模型中。例如:

# Lets declare our choices in a global scope rather than inside models
TAMANIOS = (('2x2', '2" x 2"',), ('3x3', '3" x 3"',),
           ('4x4', '4" x 4"',), ('5x5', '5" x 5"',))

CANTIDADES = (('50', '50',), ('100', '100',),
            ('150', '150',))

# Declare Forms
class StepOneForm(forms.Form):
    tamanios = forms.ChoiceField(choices=TAMANIOS)

class StepTwoForm(forms.Form):
    cantidades = forms.ChoiceField(choices=CANTIDADES)

# Third step is attached to the model directly, we will do some tweaks inside the save method of this form
class StepThreeForm(forms.ModelForm):
    class Meta:
        model = TamaniosCantidades
        fields = ('imagenes',)

# Views
# First Two steps are basic FormViews, because it will be used just to store data in session
class StepOneView(FormView):
    form_class = StepOneForm
    template_name = 'step_one.html'
    success_url = '/step-two/'

    def get_initials(self):
         # pre-populate form if someone goes back and forth between forms
         initial = super(StepOneView, self).get_initial()
         initial['tamanios'] = self.request.session.get('tamanios', None)
         return initial

    def form_valid(self, form):
        # In form_valid method we can access the form data in dict format
        # and will store it in django session
        self.request.session['tamanios'] = form.cleaned_data.get('tamanios')
        return HttpResponseRedirect(self.get_success_url())


class StepTwoView(FormView):
    form_class = StepTwoForm
    template_name = 'step_two.html'
    success_url = '/step-three/'


    def get_initials(self):
         # pre-populate form if someone goes back and forth between forms
         initial = super(StepOneView, self).get_initial()
         initial['cantidades'] = self.request.session.get('cantidades', None)
         return initial

    def form_valid(self, form):
        # same as StepOneView
        self.request.session['cantidades'] = form.cleaned_data.get('cantidades')
        return HttpResponseRedirect(self.get_success_url())


# here we are going to use CreateView to save the Third step ModelForm
class StepThreeView(CreateView):
    form_class = StepThreeForm
    template_name = 'step_three.html'
    success_url = '/thank-you/'

    def form_valid(self, form):
        form.instance.tamanios = self.request.session.get('tamanios')  # get tamanios from session 
        form.instance.cantidades = self.request.session.get('cantidades')  # get cantidades from session 
        del self.request.session['cantidades']  # delete cantidades value from session
        del self.request.session['tamanios']  # delete tamanios value from session
        self.request.session.modified = True
        return super(StepThreeView, self).form_valid(form)

# template
# step_one.html, step_two.html, step_three.html can be all the same as following code
<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Submit">
</form>

# urls

path('step-one/', StepOneView.as_view()),
path('step-two/', StepTwoView.as_view()),
path('step-three/', StepThreeView.as_view()),
path('thank-you/', TemplateView.as_view(template_name="thank-you.html")),

采用这种方法,您无需分割模型,并将数据存储在最后一步中。 FYI:其未经测试的代码,但我希望它能为您提供正确的实施方向。