Django : 양식을 사용하는 하나의 템플릿에 여러 모델 [닫기]


114

지원 티켓 추적 앱을 만들고 있으며 한 페이지에서 만들고 싶은 몇 가지 모델이 있습니다. 티켓은 ForeignKey를 통해 고객에게 속합니다. 메모는 ForeignKey를 통한 티켓에도 속합니다. 고객을 선택하거나 (완전히 별도의 프로젝트) 새 고객을 만든 다음 티켓을 만들고 마지막으로 새 티켓에 할당 된 메모를 만들 수있는 옵션을 갖고 싶습니다.

저는 Django를 처음 접하기 때문에 매번 새로운 기능을 시도하면서 반복적으로 작업하는 경향이 있습니다. ModelForms를 사용해 보았지만 일부 필드를 숨기고 복잡한 유효성 검사를 수행하고 싶습니다. 내가 찾고있는 제어 수준은 폼셋이 필요하거나 피하려고하는 지루하고 수작업으로 코딩 된 템플릿 페이지로 완성 된 모든 것을 수작업으로하는 것 같습니다.

내가 놓친 멋진 기능이 있습니까? 누군가 formset 사용에 대한 좋은 참조 또는 예제가 있습니까? 나는 주말 내내 그들을 위해 API 문서에 대해 보냈고 여전히 실마리가 없습니다. 모든 것을 분해하고 직접 코딩하면 디자인 문제입니까?


먼저 고객 양식의 유효성을 검사하고 유효한 경우 request.POST (new_data = request.POST.copy ())에서 사본을 생성 한 다음 (유효한 고객 양식에서) 고객 ID를 가져오고 new_data를 업데이트하여 customer id는 foreignkey 필드에 대한 값입니다 (모델의 고객 일 수 있음). 그리고 마지막으로 두 번째 양식을 검증하기 위해 new_data를 고려하십시오 (티켓)
Negar37

답변:


87

이것은 실제로 ModelForms 로 구현하기가 그리 어렵지 않습니다 . 따라서 Forms 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
btw, 나는 formset이 당신이 설명한 문제에 대한 좋은 해결책이라고 생각하지 않습니다. 나는 항상 모델의 여러 인스턴스를 나타내는 데 사용했습니다. 예를 들어 신청 양식이 있고 참조 모델의 인스턴스가 3 개인 양식 세트를 만들기 위해 3 개의 참조를 원합니다.
Jason Christa

1
당신이 그것을하는 방식으로 .is_valid () 호출은 단락되지 않습니다. 단락 시키려면 'and'까지 .is_valid () 함수 호출을 지연시켜야합니다.
거짓말 라이언

66

나는 하루 전에 거의 같은 상황에 처해 있었고 여기에 내 2 센트가 있습니다.

1) 여기에서 단일 형식으로 여러 모델 항목의 가장 짧고 간결한 데모를 찾았습니다. http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .

간단히 말해서 : 각 모델에 대한 양식을 만들고 keyarg를 <form>사용하여 단일으로 템플릿에 모두 제출하고 prefix뷰 핸들 유효성 검사를받습니다. 종속성이있는 경우에는 "부모"모델을 종속성 전에 저장하고 "자식"모델의 저장을 커밋하기 전에 부모의 ID를 외래 키로 사용하십시오. 링크에는 데모가 있습니다.

2) 아마도 formset을 이길 수 있지만 내가 탐구 한 한, formset은 주로 동일한 모델의 여러 개를 입력하는 데 사용 되며 선택적으로 외래 키로 다른 모델 / 모델에 연결될 수 있습니다. 그러나 둘 이상의 모델 데이터를 입력하는 기본 옵션이없는 것으로 보이며 이는 formset의 의미가 아닙니다.


26

나는 아주 최근에 몇 가지 문제가 있었고 이것을하는 방법을 알아 냈습니다. Primary, B, C의 세 가지 클래스가 있고 B, C에 primary에 대한 외래 키가 있다고 가정합니다.

    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',) 문을 얻지 못했습니다. 기본이란 무엇입니까? CustomerConfig 및 Contract 모델이 2 개있는 경우. 계약에는 CustomerConfig에 대한 외래 키가 있습니다. customer_config = models.ForeignKey ( 'CustomerPartnerConfiguration')과 같이 '기본'이란 무엇입니까?
pitchblack408

10

MultiModelFormGnudiff의 답변에django-betterforms 설명 된 작업을 수행하는 편리한 래퍼 입니다. 단일 형식으로 사용되는 투명하게 (적어도 기본 사용을 위해) 단일 클래스로 일반을 래핑 합니다. 아래 문서에서 예제를 복사했습니다.ModelForm

# 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-betterformsMultiModelForm의 답변을 통해 오는 전에 클래스입니다. 그들의 솔루션은 정말 좋아 보이지만 한동안 업데이트되지 않은 것 같습니다. 이 @jozxyqk를 여전히 사용하고 있습니까? 문제가 있습니까?
enchance

@enchance 몇 년이 지났습니다. 그 당시에는 편리하고 더 나은 옵션 중 하나라는 것을 알았습니다. 너무 화려하지 않으면 시간이 절약됩니다. 사용자 정의를 시작하고 사소하지 않은 양식을 작성하고 싶을 때 직접 롤링하는 것이 더 쉬울 것이라고 상상할 수 있습니다. 뷰에서 폼과 컨텍스트를 쉽게 혼합하는 것이 장고에서 놓친 첫 번째 기능입니다.
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방법입니다.

    이 부분이 합리적으로 잘 작동하는지 확인하십시오.

이 작업이 완료되면 기본 제공 관리 인터페이스에서 벗어날 수 있습니다.

그런 다음 단일 웹 페이지에서 부분적으로 관련된 여러 양식으로 속일 수 있습니다. 이것은 단일 페이지에 모든 양식을 표시하는 템플릿 항목입니다.

그런 다음 다양한 형식을 읽고 유효성을 검사하고 다양한 객체 saves ()를 수행하는 뷰 함수를 작성해야합니다.

"모든 것을 분해하고 직접 코딩하면 디자인 문제입니까?" 아니, 그다지 유익하지 않은 시간이 많습니다.


그러므로 나는 그것을하지 않는 방법을 모르겠어요
orokusaki

1
@orokusaki : 무엇을 더 원하십니까? 그것은 해결책을 설명하는 것 같습니다. 더 무엇을 말해야합니까? 질문은 모호하므로 실제 코드를 제공하기가 어렵습니다. 불평하지 말고 개선을위한 제안을 제공하십시오. 당신은 무엇을 제안합니까?
S.Lott 2011 년

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.