Get formclass from variable

I’m trying to load a form based on database value.

If I create a variable, it works, but when pulling the info in from the database, I get the error TypeError: ‘str’ object is not callable

my form name is EnvelopeForm.


formname = EnvelopeForm

form = formname(request.POST, instance=item)

works flawlessly.


loadform = Category.objects.get(id=cat)

formname = loadform.formname

form = formname(request.POST, instance=item)

throws the error that TypeError: ‘str’ object is not callable

is it possible to do this, or what would be the best way to select a form based on a users input?

You would need to use the getattr function to get the class based upon its name.

How you need to do this will depend upon how you’re importing your forms.

Note that for this to work:

either EnvelopeForm needs to be defined in this same file, or you’re importing it from a module.

It’s being imported from .forms

from .forms import EnvelopeForm

The form:

class EnvelopeForm(forms.ModelForm):

    class Meta:
        model = PriceSheet
        fields = [
            'description', 'set_per_book', 'pages_per_book', 'qty_of_sheets', 'original_size', 'press_size', 'press_size_per_parent',
            'flat_size', 'finished_size', 'gangup', 'overage', 'output_per_sheet', 'parent_sheets_required', 'side_1_clicks', 'side_2_clicks', 'paper_stock', 'price_per_m', 
            'step_workorder_price', 'step_reclaim_artwork_price', 'step_send_to_press_price', 'material_cost', 'material_markup_percentage', 'material_markup', 'step_print_cost_side_1', 
            'step_print_cost_side_1_price', 'step_print_cost_side_2', 'step_id_count_price', 'step_print_cost_side_2_price', 'step_count_package_price', 'step_delivery_price', 'step_packing_slip_price', 'price_total', 'price_total_per_m', 'misc1_description', 'misc1_price', 'misc2_description', 'misc2_price', 'misc3_description',
            'misc3_price', 'misc4_description', 'misc4_price'
        ]
        labels = {
            'set_per_book':'Qty',
        }

The Category Model

class Category(models.Model):
    name = models.CharField('Name', max_length=100, blank=True, null=True)
    description = models.CharField('Description', max_length=100, blank=True, null=True)
    design_type = models.BooleanField('Design Type', blank=True, null=True)
    formname = models.CharField('Form', max_length=100, blank=True, null=True)
    modal = models.BooleanField('Modal', blank=True, null=True)
    template = models.BooleanField('Template', blank=True, null=True, default=False)

    def __str__(self):
        return self.name

You can retrieve the class from the dict returned by locals().
e.g.

loadform = Category.objects.get(id=cat)
formname = locals()[loadform.formname]
form = formname(request.POST, instance=item)

After doing that, I get:

formname = locals()[loadform.formname]
KeyError: 'EnvelopeForm'

Are you using a CBV or an FBV?

I’m not sure… I’m pretty new to Django. Looking up the differences, I think it’s a FBV.

Please post the view where you’re trying to do this.

The view is a hot mess, but this is what I currently have working

# ####Working edititem
def edititem(request, id, pk, cat,):
    if request.method == "POST":
        workorderitem = KruegerJobDetail.objects.get(workorder_item=pk)
        obj = get_object_or_404(KruegerJobDetail, pk=workorderitem.id)
        form = KruegerJobDetailForm(request.POST, instance=obj)
        workorder = request.POST.get('workorder')
        edited = 1
        if form.is_valid():
            obj = form.save(commit=False)
            obj.workorder_id = workorder
            obj.edited = edited
            obj.save()
            #update workorderitem table
            lineitem = WorkorderItem.objects.get(id=pk)
            lineitem.pricesheet_modified = edited
            lineitem.description = obj.description
            lineitem.quantity = obj.set_per_book
            #lineitem.unit_price = 
            #lineitem.total_price = obj.price_total
            lineitem.save() 
        else:
            print(form.errors)
        return redirect('workorders:overview', id=obj.hr_workorder)
    if request.htmx:
        print('HTMX')
        print(pk)
        item = get_object_or_404(KruegerJobDetail, workorder_item=pk)
        if cat == 6:
            form = EnvelopeForm(instance=item)
        if cat == 10:
            form = NCRForm(instance=item)
        if cat == 4:
            form = testform(instance=item)
        context = {
            'form':form
    }
        return render (request, "pricesheet/modals/edit_item.html", context)
    else:
        print('Not HTMX')
        modified = WorkorderItem.objects.get(pk=pk)
        internal_company = modified.internal_company
        #If new lineitem, load default pricing template
        if not modified.pricesheet_modified:
            #If there is a subcategory, load the pricing template for subcategory
            if modified.item_subcategory:
                description = WorkorderItem.objects.get(pk=pk)
                description = description.description
                item = get_object_or_404(PriceSheet, subcategory=modified.item_subcategory)
                formdata = ''
            #Otherwise load category template
            else:
                item = get_object_or_404(PriceSheet, category=cat) 
        #If item contains custom pricing load that               
        else:
            item = get_object_or_404(KruegerJobDetail, workorder_item=pk)
            #formdata loads static data to template
            formdata = KruegerJobDetail.objects.get(workorder_item=pk)
            print('pk')
            print(pk)
            description = item.description
        if cat == 6:
            form = EnvelopeForm(instance=item)
        if cat == 10:
            form = NCRForm(instance=item)
        #If paper is selected, load that
        selected_paper = form.instance.paper_stock_id
        print(selected_paper)
        try:
            selected_paper = PaperStock.objects.get(id=selected_paper)
            print(selected_paper.description)
            print(selected_paper)
        except: 
            selected_paper = ''
        #populate paper list
        papers = PaperStock.objects.all()
        context = {
            'form':form,
            'formdata':formdata,
            'description':description,
            'papers':papers,
            'selected_paper':selected_paper,
            'workorder_id':id,
            'internal_company':internal_company,

    }
        return render(request, 'pricesheet/templates/master.html', context)

This view throws the error:

def edititem(request, id, pk, cat,):
    if request.method == "POST":
        workorderitem = KruegerJobDetail.objects.get(workorder_item=pk)
        obj = get_object_or_404(KruegerJobDetail, pk=workorderitem.id)
        form = KruegerJobDetailForm(request.POST, instance=obj)
        workorder = request.POST.get('workorder')
        edited = 1
        if form.is_valid():
            obj = form.save(commit=False)
            obj.workorder = workorder
            obj.edited = edited
            obj.save()
            #update workorderitem table
            lineitem = WorkorderItem.objects.get(id=pk)
            lineitem.pricesheet_modified = edited
            lineitem.description = obj.description
            lineitem.quantity = obj.set_per_book
            #lineitem.unit_price = 
            #lineitem.total_price = obj.price_total
            lineitem.save() 
        else:
            print(form.errors)
        return redirect('workorders:overview', id=obj.hr_workorder)
    # if request.htmx:
    #     print('HTMX')
    #     print(pk)
    #     item = get_object_or_404(KruegerJobDetail, workorder_item=pk)
    #     testform = EnvelopeForm
    #     if cat == 6:
    #         form = testform(instance=item)
    #     if cat == 10:
    #         form = NCRForm(instance=item)
    #     if cat == 4:
    #         form = testform(instance=item)
    #     context = {
    #         'form':form
    # }
    #     return render (request, "pricesheet/modals/edit_item.html", context)
    # else:
    #if not request.HTMX:
    print('Not HTMX')
    modified = WorkorderItem.objects.get(pk=pk)
    internal_company = modified.internal_company
    #If new lineitem, load default pricing template
    if not modified.pricesheet_modified:
        #If there is a subcategory, load the pricing template for subcategory
        if modified.item_subcategory:
            description = WorkorderItem.objects.get(pk=pk)
            description = description.description
            item = get_object_or_404(PriceSheet, subcategory=modified.item_subcategory)
            formdata = ''
        #Otherwise load category template
        else:
            item = get_object_or_404(PriceSheet, category=cat) 
    #If item contains custom pricing load that               
    else:
        item = get_object_or_404(KruegerJobDetail, workorder_item=pk)
        #formdata loads static data to template
        formdata = KruegerJobDetail.objects.get(workorder_item=pk)
        print('pk')
        print(pk)
        description = item.description
    ############################################### This is the area of discussion
    #Get form to load from category
    loadform = Category.objects.get(id=cat)
    #formname = loadform.formname
    formname = locals()[loadform.formname]
    form = formname(instance=item)
    ################################################
    # if cat == 6:
    #     form = testform(instance=item)
    # if cat == 10:
    #     form = NCRForm(instance=item)
    #If paper is selected, load that
    selected_paper = form.instance.paper_stock_id
    print(selected_paper)
    try:
        selected_paper = PaperStock.objects.get(id=selected_paper)
        print(selected_paper.description)
        print(selected_paper)
    except: 
        selected_paper = ''
    #populate paper list
    papers = PaperStock.objects.all()
    context = {
        'form':form,
        'formdata':formdata,
        'description':description,
        'papers':papers,
        'selected_paper':selected_paper,
        'workorder_id':id,
        'internal_company':internal_company,

}
    return render(request, 'pricesheet/templates/master.html', context)

Sorry, my mistake - you need to use globals() there, not local.

That worked. Thank you!