Django: หลายรุ่นในเทมเพลตเดียวโดยใช้แบบฟอร์ม [ปิด]


114

ฉันกำลังสร้างแอพติดตามตั๋วสนับสนุนและมีโมเดลสองสามแบบที่ฉันต้องการสร้างจากหน้าเดียว ตั๋วเป็นของลูกค้าผ่าน ForeignKey หมายเหตุเป็นของตั๋วผ่าน ForeignKey เช่นกัน ฉันต้องการมีตัวเลือกในการเลือกลูกค้า (ซึ่งเป็นโปรเจ็กต์แยกกันทั้งหมด) หรือสร้างลูกค้าใหม่จากนั้นสร้างตั๋วและสุดท้ายสร้างโน้ตที่กำหนดให้กับตั๋วใหม่

เนื่องจากฉันค่อนข้างใหม่กับ Django ฉันจึงมักจะทำงานซ้ำ ๆ พยายามใช้คุณสมบัติใหม่ ๆ ทุกครั้ง ฉันเคยเล่นกับ ModelForms แต่ฉันต้องการซ่อนบางฟิลด์และทำการตรวจสอบความถูกต้องที่ซับซ้อน ดูเหมือนว่าระดับการควบคุมที่ฉันกำลังมองหาไม่ว่าจะต้องใช้ชุดฟอร์มหรือทำทุกอย่างด้วยมือพร้อมหน้าเทมเพลตที่เขียนโค้ดด้วยมือที่น่าเบื่อซึ่งฉันพยายามหลีกเลี่ยง

มีคุณสมบัติที่น่ารักบางอย่างที่ฉันขาดหายไปหรือไม่? มีใครมีข้อมูลอ้างอิงหรือตัวอย่างที่ดีสำหรับการใช้ formets หรือไม่? ฉันใช้เวลาทั้งสัปดาห์กับเอกสาร API สำหรับพวกเขาและฉันก็ยังไม่รู้ตัว เป็นปัญหาในการออกแบบหรือไม่ถ้าฉันทำรายละเอียดและเขียนโค้ดด้วยมือ


ในตอนแรกคุณควรตรวจสอบความถูกต้องของแบบฟอร์มลูกค้าของคุณและหากถูกต้องให้สร้างสำเนาจาก request.POST (new_data = request.POST.copy ()) จากนั้นรับรหัสลูกค้า (จากแบบฟอร์มลูกค้าที่ตรวจสอบแล้ว) และด้วยการอัปเดต new_data ให้ทำ รหัสลูกค้าเป็นค่าสำหรับฟิลด์คีย์ต่างประเทศ (อาจเป็นลูกค้าในรุ่นของคุณ) และสุดท้ายพิจารณา new_data เพื่อตรวจสอบความถูกต้องของแบบฟอร์มที่สองของคุณ (ตั๋ว)
Negar37

คำตอบ:


87

นี้จริงๆไม่ยากเกินไปที่จะใช้กับModelForms สมมติว่าคุณมีแบบฟอร์ม A, B และ C คุณพิมพ์แบบฟอร์มและหน้าแต่ละแบบออกมาและตอนนี้คุณต้องจัดการ POST

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

นี่คือเอกสารสำหรับการตรวจสอบความถูกต้องที่กำหนดเอง


2
ฉันไม่คิดว่าชุดฟอร์มเป็นวิธีแก้ปัญหาที่ดีสำหรับปัญหาที่คุณอธิบายไว้ ฉันมักจะใช้มันเพื่อแสดงหลาย ๆ อินสแตนซ์ของโมเดล เช่นคุณมีแบบฟอร์มผู้สมัครและคุณต้องการการอ้างอิง 3 รายการเพื่อให้คุณสร้างชุดฟอร์มที่มี 3 อินสแตนซ์ของโมเดลอ้างอิง
Jason Christa

1
โปรดทราบว่าด้วยวิธีที่คุณทำการเรียก. is_valid () จะไม่ลัดวงจร หากคุณต้องการลัดวงจรคุณจะต้องชะลอการเรียกฟังก์ชัน. is_valid () จนกว่าจะถึง 'and'
Lie Ryan

66

ฉันเพิ่งตกอยู่ในสถานการณ์เดียวกันเมื่อวันก่อนและนี่คือ 2 เซ็นต์ของฉัน:

1) ฉันพบเนื้อหาที่สั้นที่สุดและรัดกุมที่สุดในการสาธิตรายการโมเดลหลายรายการในรูปแบบเดียวที่นี่: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .

โดยสรุป: สร้างแบบฟอร์มสำหรับแต่ละรุ่นส่งทั้งสองแบบไปยังเทมเพลตในรายการเดียว<form>โดยใช้prefixkeyarg และตรวจสอบความถูกต้องของ view handle หากมีการอ้างอิงเพียงตรวจสอบให้แน่ใจว่าคุณได้บันทึกโมเดล "พาเรนต์" ก่อนที่จะขึ้นกับและใช้ ID ของพาเรนต์สำหรับคีย์ต่างประเทศก่อนที่จะคอมมิชชันบันทึกโมเดล "ลูก" ลิงค์มีการสาธิต

2) บางที formets อาจถูกตีในการทำสิ่งนี้ แต่เท่าที่ฉันเจาะลึกแล้ว formets ส่วนใหญ่ใช้สำหรับการป้อนหลายรายการของรุ่นเดียวกันซึ่งอาจเชื่อมโยงกับโมเดล / โมเดลอื่นโดยใช้คีย์ต่างประเทศ อย่างไรก็ตามดูเหมือนว่าจะไม่มีตัวเลือกเริ่มต้นสำหรับการป้อนข้อมูลของโมเดลมากกว่าหนึ่งแบบและนั่นไม่ใช่สิ่งที่ formet น่าจะมีไว้สำหรับ


26

เมื่อเร็ว ๆ นี้ฉันมีปัญหาบางอย่างและเพิ่งรู้วิธีทำ สมมติว่าคุณมีสามคลาส Primary, B, C และ B, C มีคีย์ต่างประเทศเป็นคีย์หลัก

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

วิธีนี้จะช่วยให้คุณสามารถทำการตรวจสอบความถูกต้องตามที่คุณต้องการรวมทั้งสร้างวัตถุทั้งสามในหน้าเดียวกัน ฉันยังใช้จาวาสคริปต์และฟิลด์ที่ซ่อนอยู่เพื่ออนุญาตให้สร้างวัตถุ B, C หลาย ๆ ชิ้นในหน้าเดียวกัน


3
ในตัวอย่างนี้คุณตั้งค่าคีย์นอกสำหรับรุ่น B และ C ให้ชี้ไปที่รุ่นหลักได้อย่างไร
ผู้ใช้

ฉันมีเพียงสองรุ่นที่ฉันต้องการแสดงในรูปแบบเดียวกัน แต่ฉันไม่ได้รับคำสั่ง exclude = ('primary',) หลักคืออะไร? หากมี 2 รุ่น CustomerConfig และ Contract สัญญามีคีย์ต่างประเทศสำหรับ CustomerConfig เช่น customer_config = models.ForeignKey ('CustomerPartnerConfiguration') 'หลัก' คืออะไร?
pitchblack408

10

MultiModelFormจากdjango-betterformsเป็นเสื้อคลุมที่สะดวกในการทำในสิ่งที่อธิบายไว้ในคำตอบของ Gnudiff มันรวมModelForms ปกติในคลาสเดียวซึ่งโปร่งใส (อย่างน้อยสำหรับการใช้งานพื้นฐาน) ที่ใช้เป็นรูปแบบเดียว ฉันได้คัดลอกตัวอย่างจากเอกสารด้านล่างนี้

# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs

เพิ่งเห็นdjango-betterformsและคลาสMultiModelFormก่อนจะเจอคำตอบของคุณ โซลูชันของพวกเขาดูดีจริง แต่ดูเหมือนว่าจะยังไม่ได้รับการอัปเดตในขณะนี้ คุณยังใช้ @jozxyqk นี้อยู่หรือไม่? ปัญหาใด ๆ ?
enchance

@enchance ไม่กี่ปีแล้ว กลับมาแล้วฉันพบว่าสะดวกและเป็นหนึ่งในตัวเลือกที่ดีกว่า หากคุณไม่คิดฟุ้งซ่านเกินไปมันจะช่วยประหยัดเวลาได้ ฉันนึกภาพออกเมื่อคุณต้องการเริ่มปรับแต่งและสร้างรูปแบบที่ไม่สำคัญมันจะง่ายกว่าที่จะม้วนของคุณเอง การผสมผสานรูปแบบและบริบทในมุมมองได้อย่างง่ายดายเป็นคุณสมบัติแรกที่ฉันคิดว่าฉันพลาดใน django
jozxyqk

ขอบคุณสำหรับคนตอบ ฉันกำลังพิจารณาเพียงแค่ยกมันขึ้นมาและอาจจะอัปเดตบางสิ่งไปพร้อมกัน จากที่ฉันเห็นจนถึงตอนนี้มันใช้งานได้ดี คุณพูดถูกมันช่วยประหยัดเวลาได้มาก
enchance

5

ขณะนี้ฉันมีวิธีแก้ไขปัญหาชั่วคราว (ผ่านการทดสอบหน่วยของฉัน) เป็นทางออกที่ดีสำหรับความคิดเห็นของฉันเมื่อคุณต้องการเพิ่มฟิลด์จำนวน จำกัด จากรุ่นอื่น ๆ

ฉันขาดอะไรที่นี่?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile

3

"ฉันต้องการซ่อนบางช่องและทำการตรวจสอบความถูกต้องที่ซับซ้อน"

ฉันเริ่มต้นด้วยอินเทอร์เฟซผู้ดูแลระบบในตัว

  1. สร้าง ModelForm เพื่อแสดงฟิลด์ที่ต้องการ

  2. ขยายแบบฟอร์มด้วยกฎการตรวจสอบความถูกต้องภายในแบบฟอร์ม โดยปกติแล้วจะเป็นcleanวิธีการ

    ตรวจสอบให้แน่ใจว่าส่วนนี้ทำงานได้ดีพอสมควร

เมื่อเสร็จแล้วคุณสามารถย้ายออกจากอินเทอร์เฟซผู้ดูแลระบบในตัว

จากนั้นคุณสามารถใช้หลายรูปแบบที่เกี่ยวข้องกันบางส่วนในหน้าเว็บเดียว นี่คือเทมเพลตมากมายที่จะนำเสนอแบบฟอร์มทั้งหมดในหน้าเดียว

จากนั้นคุณต้องเขียนฟังก์ชั่นมุมมองเพื่ออ่านและตรวจสอบความถูกต้องของรูปแบบต่างๆและทำการบันทึกวัตถุต่างๆ ()

"มันเป็นปัญหาในการออกแบบหรือไม่ถ้าฉันทำลายและเขียนโค้ดด้วยมือทุกอย่าง" ไม่มันเป็นเพียงเวลามากเพื่อประโยชน์ไม่มาก


ฉันไม่รู้วิธีดังนั้นอย่าทำ
orokusaki

1
@orokusaki: คุณต้องการอะไรอีก? ดูเหมือนจะอธิบายวิธีแก้ปัญหา ควรพูดอะไรอีก คำถามคลุมเครือดังนั้นจึงยากที่จะระบุรหัสจริง แทนที่จะบ่นโปรดให้ข้อเสนอแนะเพื่อการปรับปรุง คุณแนะนำอะไร?
ล็อต

1

ตามเอกสารของ Django ชุดฟอร์มแบบอินไลน์มีไว้เพื่อจุดประสงค์นี้: "ชุดฟอร์มแบบอินไลน์เป็นเลเยอร์นามธรรมขนาดเล็กที่ด้านบนของชุดฟอร์มแบบจำลองสิ่งเหล่านี้ทำให้กรณีการทำงานกับวัตถุที่เกี่ยวข้องง่ายขึ้นผ่านทางคีย์ต่างประเทศ"

ดูhttps://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.