Django Can';不了解modelformset\u工厂和ModelBaseFormset

Django Can';不了解modelformset\u工厂和ModelBaseFormset,django,django-models,django-views,Django,Django Models,Django Views,我正在努力理解我的应用程序中的BaseModelFormSet和modelformset\u工厂。我想我明白了这个概念,但在实施过程中遇到了问题。下面的代码工作得很好,但我认为它过于复杂,因为我缺乏理解 这是可以的,但是当我想根据当前表单的用户ID(而不是登录的用户)修改电子邮件列表查询集时,我遇到了问题。基本上,这段代码会影响两个模型。第一个是族模型(和FamilyBaseFormSet),很好。第二个是家庭成员模型,它包含每个家庭成员的用户ID。如果该用户id是is_人员的一部分,那么我希望

我正在努力理解我的应用程序中的BaseModelFormSet和modelformset\u工厂。我想我明白了这个概念,但在实施过程中遇到了问题。下面的代码工作得很好,但我认为它过于复杂,因为我缺乏理解

这是可以的,但是当我想根据当前表单的用户ID(而不是登录的用户)修改电子邮件列表查询集时,我遇到了问题。基本上,这段代码会影响两个模型。第一个是族模型(和FamilyBaseFormSet),很好。第二个是家庭成员模型,它包含每个家庭成员的用户ID。如果该用户id是is_人员的一部分,那么我希望电子邮件_列表查询对他们来说是不同的(不同的过滤器)。当我修改它时,整个站点将挂起

因此,以下是我的问题: 1.有没有更简单的方法来编写此代码?是否可以删除FamilyBaseFormSet和FamilyMemberBaseFormSet?您可以看到,我拥有它们的唯一原因是修改form.fields 2.如何确定当前表单(当前家庭成员)用户id是否为员工?如果是这样,我如何适当地设置查询


class FamilyBaseFormSet(BaseModelFormSet):
    def add_fields(self, form, index):
    super(FamilyBaseFormSet, self).add_fields(form, index)
    form.fields['state'].widget.attrs['class'] = 'input-mini'
        form.fields['zip_code'].widget.attrs['class'] = 'input-small'
    form.fields['emergency_notes'].widget.attrs['rows'] = '2'
    form.fields['notes'].widget = forms.HiddenInput()
    form.fields['is_active'].widget = forms.HiddenInput()
    form.fields['family_benefits'].widget = forms.HiddenInput()

class FamilyMemberBaseFormSet(BaseModelFormSet):
    def add_fields(self, form, index):
    super(FamilyMemberBaseFormSet, self).add_fields(form, index)
        form.fields['email_list'].queryset = EmailList.objects.filter(is_active=True)
    form.fields['first_name'].widget.attrs['class'] = 'input-small'
    form.fields['first_name'].required = True
    form.fields['first_name'].error_messages = {'required': 'Please enter a first name'}
    form.fields['middle_name'].widget.attrs['class'] = 'input-mini'
    form.fields['last_name'].widget.attrs['class'] = 'input-small'
    form.fields['last_name'].required = True
    form.fields['last_name'].error_messages = {'required': 'Please enter a last name'}
    form.fields['state'].widget.attrs['class'] = 'input-mini'
    form.fields['zip_code'].widget.attrs['class'] = 'input-small'
    form.fields['gender'].widget = forms.HiddenInput()
    form.fields['family'].widget = forms.HiddenInput()
    form.fields['username'].widget = forms.HiddenInput()
    form.fields['password'].widget = forms.HiddenInput()
    form.fields['last_login'].widget = forms.HiddenInput()
    form.fields['date_joined'].widget = forms.HiddenInput()
    form.fields['family_member_role'].widget = forms.HiddenInput()
    form.fields['notes'].widget = forms.HiddenInput()
    form.fields['is_superuser'].widget = forms.HiddenInput()
    form.fields['is_active'].widget = forms.HiddenInput()
    form.fields['is_staff'].widget = forms.HiddenInput()

def manage_family_member(request):

    FamilyInlineFormSet = modelformset_factory(Family, extra=0, formset=FamilyBaseFormSet)
    FamilyMemberInlineFormSet = modelformset_factory(FamilyMember,
    extra=0, formset=FamilyMemberBaseFormSet)
    email_list_description = EmailList.objects.filter(is_active=True)
    if request.method == "POST":
    family_formset = FamilyInlineFormSet(request.POST,  request.FILES,
        queryset=Family.objects.filter(id=request.user.family.id), prefix='f')
    family_member_formset = FamilyMemberInlineFormSet(request.POST, request.FILES,
        queryset=FamilyMember.objects.filter(family=request.user.family.id), prefix='fm')
    if family_formset.is_valid() and family_member_formset.is_valid():
        family_formset.save()
        family_member_formset.save()
        return redirect('/school/thanks/')
    else:
    family_formset = FamilyInlineFormSet(queryset=Family.objects.filter(id=request.user.family.id), prefix='f')
    family_member_formset = FamilyMemberInlineFormSet(queryset=FamilyMember.objects.filter(family=request.user.family.id), prefix='fm')

    context = RequestContext(request,{
    'email_list_description': email_list_description,
    'family_formset': family_formset,
    'family_member_formset': family_member_formset,
    })
    return render_to_response("school/family/manage_family_members.html", context)

您的代码不仅比它必须要复杂得多,而且还存在一些非常重要的安全漏洞。根据其中的字段,我认为
FamilyMember
是您的自定义用户模型

假设我是一个可以编辑我的家庭的普通家庭成员。我得到了一个有很多隐藏输入的表单,包括
is\u staff
is\u superuser
。任何带有网页调试工具栏的傻瓜都可以将这些隐藏输入字段的值更改为
True
。现在我将这些字段的值更改为
True
,以表单形式发送,塔达:我是一个超级用户,可以把你的整个网站搞砸

HiddenInput
小部件不能替代实际的服务器端验证和安全性。对于隐藏的输入字段,唯一有效的用例是默认值,用户通常不应在该特定形式中更改该值,但在更改时不会造成任何安全问题,这可能是因为用户以某种方式被允许更改,或者是因为服务器覆盖更改或拒绝请求

您需要做的是在表单集中使用一个自定义表单,它只包含您想要包含的字段。对于您的
FamilyMember
型号,它可以是以下
ModelForm

class FamilyMemberForm(forms.ModelForm):
    first_name = forms.CharField(max_length=50, required=True, 
                                 widget=forms.TextInput(attrs={'class': 'input-small'}),
                                 error_messages={'required': 'Please enter a first name'})
    ...

    class Meta:
        model = FamilyMember
        fields = ['first_name', 'middle_name', 'last_name', 'state', 'zip_code']
        widgets = {
            'middle_name': forms.TextInput(attrs={'class': 'input-mini'}),
            ...
        }
这将确保用户只能编辑他们应该编辑的字段(您显式添加到表单中的字段),并将使formset类的复杂性大大降低。您可以使用
form
参数将要使用的表单传递给表单集工厂:

family_member_formset = FamilyMemberInlineFormSet(request.POST, request.FILES, 
        queryset=FamilyMember.objects.filter(family=request.user.family.id), prefix='fm',
        form=FamilyMemberForm)
至于你的第二个问题,我不完全确定你想要什么。当前家庭成员是指登录用户吗?如果是这样,这就足够了:

if request.user.is_staff:
    email_list_description = <queryset for staff members>
else:
    email_list_description = <queryset for non-staff members>
如果request.user.is\u员工:
电子邮件列表描述=
其他:
电子邮件列表描述=

如果这不是您的意思,那么您究竟在哪里使用此
电子邮件\u列表\u说明
,以及应基于哪些用户权限

我甚至没有想到你提到的安全方面。谢谢我也感谢你的其他建议。至于第二个问题,email_list_description只是列出email列表的描述。可以忽略它。form.fields['email_list'].queryset=EmailList.objects.filter(is_active=True)是我根据当前表单的FamilyMember进行筛选的内容,而不是基于登录的用户。家长将登录,加载此页面时,将显示所有家庭成员。对于每个属于联合国职员的成员,上述查询集的设置应该有所不同。