Модель сохранения волшебной формы Django с использованием нескольких форм

#python #django #django-formwizard #django-formtools

Вопрос:

У меня есть образцовый клиент:

 class Customer(models.Model):
name = models.CharField(
    verbose_name='Company name',
    max_length=100,
    unique=True
)
street = models.CharField(
    verbose_name='Street',
    null=True,
    max_length=150
)
house_number = models.IntegerField(
    verbose_name='Number',
    null=True
)
zipcode = models.CharField(
    verbose_name='Zipcode',
    max_length=6
)
city = models.CharField(
    verbose_name='City',
    max_length=100
)
phone = models.CharField(
    verbose_name='Phone',
    max_length=100
)
mobile_phone = models.CharField(
    verbose_name='Mobile',
    max_length=100
)
fax = models.CharField(
    verbose_name='Fax',
    max_length=100,
    null=True,
    blank=True
)
email = models.EmailField(
    verbose_name='E-mail'
)
website = models.CharField(
    verbose_name='Website',
    max_length=100,
    null=True,
    blank=True
)
kvk = models.CharField(
    verbose_name='KvK-number',
    max_length=20,
    help_text='s.v.p. uittreksel toevoegen, niet ouder dan 6 maanden'
)
kvk_file = models.FileField(
    verbose_name='Excerpt',
    max_length=2000,
    upload_to=kvk_directory_path,
    null=True,
)
activities = models.TextField(
    verbose_name='Main acitivities',
    null=True,
    blank=True
)
total_employees = models.IntegerField(
    verbose_name='Total employees',
    null=True,
    blank=True
)
yearly_transactions = models.IntegerField(
    verbose_name='Estimated yearly transactions',
    null=True,
    blank=True
)
order_confirmation = models.BooleanField(
    verbose_name='Order confirmation by e-mail?',
    default=False
)
confirmation_email = models.EmailField(
    verbose_name='E-mail for order confirmation',
    null=True
)
delivery_address = models.CharField(
    verbose_name='Delivery address',
    max_length=100
)
delivery_zip = models.CharField(
    verbose_name='Zipcode',
    max_length=6
)
delivery_city = models.CharField(
    verbose_name='City',
    max_length=100
)
delivery_instructions = models.TextField(
    verbose_name='Instructions',
    null=True
)
mail_address = models.CharField(
    verbose_name='PO Address',
    max_length=100,
    null=True,
    blank=True
)
mail_zip = models.CharField(
    verbose_name='PO Zipcode',
    max_length=6,
    null=True,
    blank=True
)
mail_city = models.CharField(
    verbose_name='PO City',
    max_length=100,
    null=True,
    blank=True
)
iban = models.CharField(
    verbose_name='IBAN-number',
    max_length=36
)
btw = models.CharField(
    verbose_name='BTW-number',
    max_length=50
)
cash_pin = models.BooleanField(
    verbose_name='Cash/Pin',
    help_text='(pickup-only)',
    default=False
)
invoice_mail = models.BooleanField(
    verbose_name='Invoice per mail?',
    default=False
)

invoice_street = models.CharField(
    verbose_name='Street',
    max_length=100,
    null=True
)
invoice_house_number = models.IntegerField(
    verbose_name='Number',
    null=True
)
invoice_zip = models.CharField(
    verbose_name='Zipcode',
    max_length=100,
    null=True
)
invoice_city = models.CharField(
    verbose_name='City',
    max_length=100,
    null=True
)
invoice_email = models.BooleanField(
    verbose_name='Invoice per e-mail?',
    default=False
)
invoice_emailaddress = models.EmailField(
    verbose_name='E-mail',
    null=True
)
debit = models.BooleanField(
    verbose_name='Debit',
    default=False
)
comment = models.TextField(
    verbose_name='Comments'
)

toa = models.BooleanField(
    verbose_name='I agree with the terms',
    default=True
)

def __str__(self):
    return self.name
 

этого довольно много, чтобы втиснуть в одну форму, поэтому я разделил ее на разные формы:

 class CompanyForm(ModelForm):
name = forms.CharField(
    label=_("Company name"),
    max_length=100,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
street = forms.CharField(
    label=_('Street'),
    max_length=150,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
house_number = forms.CharField(
    label=_('House number'),
    max_length=10,
    widget=forms.TextInput(attrs={'class': 'form-control right', 'placeholder': 'placeholder'})
)
zipcode = forms.CharField(
    label=_('Zipcode'),
    max_length=6,
    widget=forms.TextInput(attrs={'class': 'form-control left', 'placeholder': 'placeholder'})
)
city = forms.CharField(
    label=_('City'),
    max_length=100,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
phone = forms.CharField(
    label=_('Phone'),
    max_length=15,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Vast nmmr', 'type': 'tel'})
)
mobile_phone = forms.CharField(
    label=_('Mobiel'),
    max_length=100,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Mobiel nmmr', 'data-placeholder': 'mobiel nmmr', 'type': 'tel'})
)
fax = forms.CharField(
    label=_('Fax'),
    max_length=100,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'}),
    required=False
)
email = forms.EmailField(
    label=_('E-mail'),
    widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'placeholder', 'onfocusout': 'validatemail(this.attributes["name"].value,this.id, "email")'})
)
website = forms.CharField(
    label=_('Website'),
    max_length=100,
    required=False,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
kvk = forms.CharField(
    label=_('KvK number'),
    max_length=12,
    help_text=_('please add excerpt, not older than 6 months'),
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
activities = forms.CharField(
    label=_('Main activities'),
    widget=forms.Textarea(attrs={'class': 'form-control', 'placeholder': 'placeholder', 'rows': 2}),
    required=False
)
total_employees = forms.IntegerField(
    label=_('Total employees'),
    required=False,
    widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'placeholder', 'min': '0'})
)
yearly_transactions = forms.IntegerField(
    label=_('Estimated yearly transactions'),
    required=False,
    widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'placeholder', 'min': '0'})
)

class Meta:
    model = Customer

    fields = [
        'name',
        'street',
        'house_number',
        'zipcode',
        'city',
        'phone',
        'mobile_phone',
        'fax',
        'email',
        'website',
        'kvk',
        'activities',
        'total_employees',
        'yearly_transactions',
    ]
class AddressesForm(ModelForm):
    # Delivery Fields ----
    delivery_street = forms.CharField(
        label=_('Street'),
        max_length=100,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
    )
    delivery_number = forms.CharField(
        label=_('House number'),
        max_length=10,
        widget=forms.TextInput(attrs={'class': 'form-control right', 'placeholder': 'placeholder'})
    )
    delivery_zip = forms.CharField(
        label=_('Zipcode'),
        max_length=6,
        widget=forms.TextInput(attrs={'class': 'form-control left', 'placeholder': 'placeholder'})
    )
    delivery_city = forms.CharField(
        label=_('City'),
        max_length=100,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
    )
    delivery_instructions = forms.CharField(
        label=_('Delivery instructions'),
        required=False,
        widget=forms.Textarea(attrs={'class': 'form-control', 'placeholder': 
 'placeholder', 'rows': 2})
    )
    # ---- Delivery Fields

# Invoice Fields ----
different_invoice_address = forms.BooleanField(
    label=_('Different invoice address'),
    required=False,
    widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'placeholder': 'placeholder'})
)
invoice_mail = forms.ChoiceField(
    label=_('Invoice per mail?'),
    choices=INVOICE_PER_MAIL,
    required=False,
    widget=forms.RadioSelect(attrs={'class': 'form-check-input'}),
)
invoice_street = forms.CharField(
    label=_('Street'),
    max_length=100,
    required=False,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
invoice_house_number = forms.CharField(
    label=_('House number'),
    max_length=100,
    required=False,
    widget=forms.TextInput(attrs={'class': 'form-control right', 'placeholder': 'placeholder'})
)
invoice_zip = forms.CharField(
    label=_('Zipcode'),
    max_length=100,
    required=False,
    widget=forms.TextInput(attrs={'class': 'form-control left', 'placeholder': 'placeholder'})
)
invoice_city = forms.CharField(
    label=_('City'),
    max_length=100,
    required=False,
    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
invoice_email = forms.BooleanField(
    label=_('Invoice per e-mail?'),
    required=False,
    widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'placeholder': 'placeholder'})
)
invoice_emailaddress = forms.EmailField(
    label=_('E-mailaddress for invoices'),
    required=False,
    widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
# ---- Invoice Fields

# Mail Fields ----
mail_address = forms.CharField(
    label=_('PO number'),
    max_length=5,
    widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'}),
    required=False
)
using_mail_address = forms.BooleanField(
    label=_('Use PO?'),
    required=False,
    widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'placeholder': 'placeholder'})
)
mail_zip = forms.CharField(
    label=_('Zipcode'),
    max_length=6,
    widget=forms.TextInput(attrs={'class': 'form-control left', 'placeholder': 'placeholder'}),
    required=False
)
mail_city = forms.CharField(
    label=_('City'),
    max_length=100,
    widget=forms.TextInput(attrs={'class': 'form-control right', 'placeholder': 'placeholder'}),
    required=False
)

# ---- Mail Fields

class Meta:
    db_table = 'customers_customer'
    model = Customer
    fields = [
        'delivery_street',
        'delivery_number',
        'delivery_zip',
        'delivery_city',
        'delivery_instructions',
        'different_invoice_address',
        'invoice_mail',
        'invoice_street',
        'invoice_house_number',
        'invoice_zip',
        'invoice_city',
        'invoice_email',
        'invoice_emailaddress',
        'using_mail_address',
        'mail_address',
        'mail_zip',
        'mail_city',
    ]
class PaymentForm(ModelForm):
    # PAYMENT SPECIFICS
    iban = forms.CharField(
        label=_('IBAN number'),
        max_length=36,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder', 'onfocusout': 'alertValidIBAN(this.value)'})
    )
    btw = forms.CharField(
        label=_('Tax number'),
        max_length=50,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
    )
    cash_pin = forms.BooleanField(
        label=_('Cash/Card'),
        help_text=_('(pick-up only)'),
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'placeholder': 'placeholder'})
    )
    debit = forms.BooleanField(
        label=_('Automatic collection, permission to cancel'),
        help_text=_('(invoice sum will be debited within 14 days)'),
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'placeholder': 'placeholder'})
    )
    # --- PAYMENT SPECIFICS

# MISC ---
order_confirmation = forms.ChoiceField(
    label=_('Order confirmation per mail?'),
    choices=ORDER_CONFIRMATION,
    required=False,
    widget=forms.RadioSelect(attrs={'class': 'form-check-input'}),
)
confirmation_email = forms.EmailField(
    label=_('E-mail'),
    required=False,
    widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'placeholder', 'onfocusout': 'validatemail(this.attributes["name"].value,this.id, "confemail")'})
)
comment = forms.CharField(
    label=_('Comments and complements'),
    required=False,
    widget=forms.Textarea(attrs={'class': 'form-control', 'placeholder': 'placeholder'})
)
toa = forms.BooleanField(
    label=_('I am authorized to sign and agree to the delivery-, payment-, and rental terms of Wijngo Holland b.v.v and hereby declare that the forms is filled correctly'),
    widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'placeholder': 'placeholder'})
)

# --- MISC
class Meta:
    db_table = 'customers_customer'
    model = Customer
    fields = [
        'debit',
        'iban',
        'btw',
        'cash_pin',
        'order_confirmation',
        'confirmation_email',
        'comment',
        'toa',
    ]
 

теперь я застрял на построении метода «готово». В документации не совсем ясно, как это сделать, когда вы используете несколько форм для одной модели.

I could do customer = form_list[0].save(commit=False)

and then go by all the rest of the fields one by one:

customer.delivery_street = form_list[1].cleaned_data['delivery_street']

customer.delivery_number = form_list[1].cleaned_data['delivery_number']

but thats alooooot and feels too tedious than necessary.

How do I got about doing this?

views.py:

     FORMS = [
    ("company", CompanyForm),
    ("addresses", AddressesForm),
    ("references", ReferenceFormSet),
    ("contacts", ContactFormSet),
    ("payment", PaymentForm),
    # ("upload", DocumentForm),

]

TEMPLATES = {
    "company":    "form/step-1.html",
    "addresses":  "form/step-2.html",
    "references": "form/step-3.html",
    "contacts":   "form/step-4.html",
    "payment":    "form/step-5.html",
    # "upload":     "form/step-6.html",

}
class RegisterWizard(SessionWizardView):
def get_template_names(self):
    return [TEMPLATES[self.steps.current]]

def render_goto_step(self, *args, **kwargs):
    form = self.get_form(data=self.request.POST)
    # form = self.get_form(data=self.request.POST, request.FILES)
    self.storage.set_step_data(self.steps.current, self.process_step(form))
    # self.storage.set_step_files(self.steps.current, self.process_step_files(form))
    return super().render_goto_step(*args, **kwargs)

file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'bestanden'))

def get_context_data(self, form, **kwargs):
    context = super().get_context_data(form=form, **kwargs)
    context.update({'callme_forms': CallMeForms(prefix='callme')})
    # context.update({'document_forms': DocumentForm(prefix='document')})

    return context

def done(self, form_list, **kwargs):
    customer = form_list[0].save(commit=False)

    if form_list[1].cleaned_data['different_invoice_address'] is False:
       # fill invoice_data with delivery_data

    return render(
        self.request, 'thank-you.html', {
            'form_data': [form.cleaned_data for form in form_list],
        }
    )
 

urls.py:

 from django.contrib import admin
from django.urls import path

from . import views
from .views import FORMS

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.RegisterWizard.as_view(FORMS), name='index'),
    path('thank-you/', views.thank_you, name='thank_you'),
    path('privacy-verklaring/', views.privacy_policy, name='privacy_policy'),
    path('algemene-voorwaarden/', views.toa, name='toa'),
    path('save_callme/', views.save_callme, name='save_callme')
]