Django modelformset前缀确实将数据与特定的modelformset相关联

时间:2019-12-02 21:45:31

标签: django django-forms

在预算应用程序中,显示两种项目类型的模型形式集,每种项目类型的两个模型形式集,共4个模型形式集。为了区分不同项目的模型形式集,每个模型形式集都有其project_type作为前缀。在post方法中,当我尝试接收每种项目类型的modelformset实例时,前缀似乎不起作用,因此我从每个modelformset中的所有modelformses获取所有数据!

class AddProFormaRates(DevelopmentView):
    ''' Adds budget rates for the budget in the kwargs 
        1) Given the budget, find the project types for the budget
        2) For each project type, create a modelformset from ServiceRate
    '''

    template_name = 'project/budget.html'


    # Create modelformset for the_project_type:            
    RateForms = modelformset_factory(ServiceRate,
                                     formset = BaseRateFormSet,
                                     form = RateForm,
                                     extra = 0)


    def dispatch(self, *args, **kwargs):
        return super(AddProFormaRates, self).dispatch(*args, **kwargs)

    ...

    def post(self, request, *args, **kwargs):
        context = super(AddProFormaRates, self).get(request, *args, **kwargs)

        # 1) Get the project types for the budget

        budget_id = kwargs.get('pk', None)
        if budget_id:
            the_budget = budget.objects.get(id = budget_id)
        else:
            the_budget = context.get('budget', None)

        the_project = request.user.project
        the_project_types = the_budget.budget_project_types.all()

        # 2) Create modelformsets for each project type 

        rate_formsets = []
        formsets_errors = {}
        the_forms = []

        formsets_data = {}

        expense_rate_forms_errors = None
        revenue_rate_forms_errors = None

        expense_rate_forms_non_form_errors = None
        revenue_rate_forms_non_form_errors = None

        development_type_data = {}


        for the_project_type in the_project_types:
            ### SETUP
            the_form_kwargs = {'project': the_project, 'project_type': the_project_type,}   

            expense_rate_kwargs = {'project_type': the_project_type, 'rate_type': 'expense' }
            revenue_rate_kwargs = {'project_type': the_project_type, 'rate_type': 'revenue' }

            expense_prefix = the_project_type.short_name + '_expense'
            revenue_prefix = the_project_type.short_name + '_revenue'

            ### FORMSET
            expense_rate_forms = self.RateForms(request.POST if any(request.POST) else None, 
                                                prefix = expense_prefix,    # <=== Notice the prefix
                                                form_kwargs = the_form_kwargs, 
                                                **expense_rate_kwargs )

            revenue_rate_forms = self.RateForms(request.POST if any(request.POST) else None,
                                                prefix = revenue_prefix,    # <=== Notice the prefix
                                                form_kwargs = the_form_kwargs, 
                                                **revenue_rate_kwargs )


            # ---------------- For debugging purposes: ------------------
            expense_rate_data = expense_rate_forms.data
            expense_formset_prefix = expense_rate_forms.prefix
            num_expense_data = len(expense_rate_data)

            revenue_rate_data = revenue_rate_forms.data         
            revenue_formset_prefix = revenue_rate_forms.prefix
            num_revenue_data = len(revenue_rate_data)

            # ---------------- End of debugging code ------------------

            ## Dealing with expense_rate_forms 
            if expense_rate_forms.has_changed() and expense_rate_forms.is_valid():

                expense_rates_cd = expense_rate_forms.cleaned_data
                number_of_expense_rate_forms = expense_rate_forms.total_form_count()

                expense_rate_instances = expense_rate_forms.save()

                formsets_data.update( { expense_rate_forms.prefix: expense_rate_forms.data })

                # Setting Messages:
                success_message = 'The rate for ' + expense_rate_forms.prefix + ' was updated correctly.'
                messages.success(request, success_message)

            else:
                ### ERRORS: Creating a structure representing errors for each formset
                #   If a formset has an error, collect form and non_form_errors of the formset in formsets_error dictionary
                #   showing which formset has which errors

                expense_rate_forms_errors = expense_rate_forms.errors   # formset_errors is a dictionary of errors
                expense_rate_forms_non_form_errors = expense_rate_forms.non_form_errors()

                # Setting Messages
                error_message = 'The rate for ' + revenue_rate_forms.prefix + ' has errors!'
                messages.error(request, error_message)

                formsets_errors.update( {expense_rate_forms.prefix: [expense_rate_forms_errors, expense_rate_forms_non_form_errors] } )
                the_forms.append(expense_rate_forms)

            ## Dealing with revenue_rate_forms
            if revenue_rate_forms.is_valid():
                revenue_rates_cd = revenue_rate_forms.cleaned_data
                number_of_revenue_rate_forms = revenue_rate_forms.total_form_count()
                formsets_data.update( { revenue_rate_forms.prefix: revenue_rate_forms.data })
                revenue_rate_instances = revenue_rate_forms.save()

            else:
                ### ERRORS: Creating a structure representing errors for each formset
                #   If a formset has an error, collect form and non_form_errors of the formset in formsets_error dictionary
                #   showing which formset has which errors
                revenue_rate_forms_errors = revenue_rate_forms.errors   # formset_errors is a dictionary of errors
                revenue_rate_forms_non_form_errors = revenue_rate_forms.non_form_errors()
                formsets_errors.update( {revenue_rate_forms.prefix: [revenue_rate_forms_errors, revenue_rate_forms_non_form_errors] } )

                the_forms.append(revenue_rate_forms)

            rate_formsets.append(expense_rate_forms)
            rate_formsets.append(revenue_rate_forms)

        if expense_rate_forms_errors or expense_rate_forms_non_form_errors or revenue_rate_forms_errors or revenue_rate_forms_non_form_errors:

            summary_card_data = summary_data(the_budget)
            header = 'Rates for ' + the_budget.budget_name

            c = {#'formset': proforma_rate_forms,
                 'formsets': rate_formsets,
                 'record': the_budget,
                }

            context.update(c)


            return render(request, self.template_name, context)

        else:

            return HttpResponseRedirect(reverse('project:detail_view_budget', kwargs={'pk': the_budget.pk }))

问题是当添加和更改收入数据时,该数据没有保存,但是出现两个消息,表明费用表已成功更改!所以,我写了两行(# ---------------- For debugging purposes: ------------------)这是显示的内容:

expense_rate_data=
{ ...
 'WRMRN_expense-9-UOM': '% of loan',
 'WRMRN_expense-9-id': '4707',
 'WRMRN_expense-9-proformaitem': '3689',
 'WRMRN_expense-9-rate': '0.50',
 'WRMRN_expense-INITIAL_FORMS': '15',
 'WRMRN_expense-MAX_NUM_FORMS': '1000',
 'WRMRN_expense-MIN_NUM_FORMS': '0',
 'WRMRN_expense-TOTAL_FORMS': '15',
 'WRMRN_expense-__prefix__-UOM': '$/BSF',
 'WRMRN_expense-__prefix__-costcode': '',
 'WRMRN_expense-__prefix__-item_name': '',
 'WRMRN_expense-__prefix__-rate': '',
 'WRMRN_revenue-0-UOM': '$/BSF',
 'WRMRN_revenue-0-id': '4712',
 'WRMRN_revenue-0-proformaitem': '3694',
 'WRMRN_revenue-0-rate': '3.40',
 'WRMRN_revenue-1-UOM': 'percent',
 'WRMRN_revenue-1-id': '4760',
 'WRMRN_revenue-1-proformaitem': '3701',
 'WRMRN_revenue-1-rate': '4.00',
...
}

revenue_rate_data = { ...
 'WRMRN_expense-9-UOM': '% of loan',
 'WRMRN_expense-9-id': '4707',
 'WRMRN_expense-9-proformaitem': '3689',
 'WRMRN_expense-9-rate': '0.50',
 'WRMRN_expense-INITIAL_FORMS': '15',
 'WRMRN_expense-MAX_NUM_FORMS': '1000',
 'WRMRN_expense-MIN_NUM_FORMS': '0',
 'WRMRN_expense-TOTAL_FORMS': '15',
 'WRMRN_expense-__prefix__-UOM': '$/BSF',
 'WRMRN_expense-__prefix__-costcode': '',
 'WRMRN_expense-__prefix__-item_name': '',
 'WRMRN_expense-__prefix__-rate': '',
 'WRMRN_revenue-0-UOM': '$/BSF',
 'WRMRN_revenue-0-id': '4712',
 'WRMRN_revenue-0-proformaitem': '3694',
 'WRMRN_revenue-0-rate': '3.40',
 'WRMRN_revenue-1-UOM': 'percent',
 'WRMRN_revenue-1-id': '4760',
 'WRMRN_revenue-1-proformaitem': '3701',
 'WRMRN_revenue-1-rate': '4.00',
  ...
}
...
num_revenue_data      209
num_expense_data      209

为完整起见,这是自定义modelformsetform类。

class BaseRateFormSet(BaseModelFormSet):
    ''' Changes the behavior of modelformset to allow for customization 
        based on multiple variables such as budget
    '''

    def __init__(self, *args, **kwargs):
        ''' Changes the display behavior of rate forms based on the budget '''

        the_project_type = kwargs.pop('project_type', None)
        the_budget = kwargs.pop('budget', None)
        rate_type = kwargs.pop('rate_type', None)

        super(BaseRateFormSet, self).__init__(*args, **kwargs)

        if the_project_type:
            relevant_rates = self.queryset = ServiceRate.objects.filter(proformaitem__development_type = the_project_type, proformaitem__item_type = rate_type)

class RateForm(ModelForm):
    ''' Modelform for ServiceRate model 
        This form is designed for a sepcial use in which certain fields
        are disabled with both the intial value and data set on them. This means that 
        the user is not able to change those preset values.  Note that because some 
        fields are disabled, it is important that we make sure there is an initial 
        value provided for them to display.

    '''
    error_css_class = 'error'          # To add CSS class 'error' to fields that trigger the error
    required_css_class = 'required'   # To add CSS class 'required' to fields that are required

    costcode = forms.CharField(max_length = 40, required = True)
    item_name = forms.CharField(max_length = 40, required = False)


    class Meta:
        model = ServiceRate
        fields = ['costcode', 'item_name', 'rate', 'UOM',]

    def save(self, commit = True, *args, **kwargs):
        ''' Saves the rate record and the UOM. Because in the current implementation of the RateForm there is costcode and item_name,
            we have to save these as well.

        '''
        instance = super(RateForm, self).save( *args, **kwargs)  # commit = False ensures it is not saved to the database just yet

        prefix = self.prefix
        the_project = self.user.project

        # The form prefix has revenue or expense information in it (e.g. prefix: 'WRMRN_revenue-1')
        if 'revenue' in prefix or 'reven' in prefix:
            the_item_type = 'revenue'

        else:
            the_item_type = 'expense'

        cd = self.cleaned_data
        cleaned_costcode_str = cd.get('costcode', None)
        cleaned_item_name = cd.get('item_name', None)

        # Create the costcode:
        the_costcode, created_costcode = get_or_create_costcode(cleaned_costcode_str, the_project)

        # Now that we have the costcode and rate, we can create the proforma item
        the_proformaitem, created_proformaitem = budgetitem.objects.get_or_create(item_name = cleaned_item_name,
                                                                                   costcode = the_costcode,
                                                                                   rate = instance,
                                                                                   #development_type__proforma__development_project__project = the_project,
                                                                                   project_type = the_project_type,
                                                                                   defaults = {
                                                                                       'item_name': cleaned_item_name,
                                                                                       'costcode': the_costcode,
                                                                                       'rate': instance,
                                                                                       'item_type': the_item_type,
                                                                                       'project_type': the_project_type,
                                                                                      } 
                                                                                   )

        return instance

为什么前缀不起作用?我应该从instant_rate_data内的sense_rate_forms获取数据,但是所有数据都传递给它。为什么会这样?

0 个答案:

没有答案