Correct view construction

Hi guys,

I’d like some advice regarding view construction for my POS project.

I know how to create basic FBV views. But I now want a HTML template that a user can interact with that can facilitate the following:

  1. Render a HTML template with data from multiple models relating to a pk
  2. Render forms on the HTML template
  3. Allow the user to submit/post data to the respective models via their forms
  4. Select a table record from the HTML page, edit the record and save/delete the record

As you can see, there is a laundry list of tasks that the HTML page must handle.

So my question is, what’s the best way to go about doing all this whilst maintaining functionality and security? As far as I can see, I have 3 options:

  1. I wrap all this within a single complex CBV (Class Based View)
  2. Create a CBV to handle some of the tasks and supplement in other FBV (Function Based Views)
  3. Create a bunch of FBVs to handle each individual element

Any advice is welcome.

Thanks!

Welcome @Christiaan107 !

Actually, there are at least 5 options. In addition to the three you listed, you can add:

  • Create a bunch of CBVs, one to handle each form.
  • Create one complex FBV.

Functionally, there is no difference between an FBV and a CBV. There is nothing done in one that can’t be done in the other, and that equivalence works both ways. (This equivalence extends to all attributes of “functionality and security”.)

Keep in mind the fundamental definition of a view: “A view is a function that accepts an http request and returns a response.” Whether this function is defined as a function, or as a function within a class, does not materially affect any behavior.

So, this decision is what I refer to as an “Architectural decision”. What I mean by this is that this is a decision that tends to be more based on non-technical factors. What are you more comfortable using? Are there reasons why a CBV will make your code “better” from your perspective? (More readable? More maintainable?) That’s something for you to decide for the group that is going to be working on this.

Having said all that, if this page consists of multiple HTML forms with each form containing a different Django form, and only one form being submitted at a time, I’d be inclined to create multiple views to handle the submission, each view handling one form.

Whether I do this with FBVs or CBVs depends upon the system overall. (In many cases, we use the Django-provided CBVs as a starting point, not an ending point - often adding additional custom functionality needed by multiple views.)

So let’s say there are 4 forms on this page. I’d be creating 5 views. One view to create this page, and one view to handle the submission of each form.

Finally, I’m not sure I understand how item 4 fits into this, at least relative to the previous 3 items. Is the selection for this used to create this page with the multiple forms? Or is this related to a different page.

1 Like

Thanks @KenWhitesell for your thoughts and feedback.

The beautiful thing about Django is there is multiple ways to achieve something.

What I want to know is, is there an “industry standard” way of doing the above.

When I constructed the models & forms for example, I was careful to mind data normalization rules/guidelines.

What I’m currently doing is having a primary CBV that facilitates most of the requirements, namely (Rendering the HTML template, “get” the relative forms and data from the models and “post” saving the updated record data whilst still having the ability to initiate any FBV’s relative to the main pk.

By doing most of the work in the CBV, I avoid having to constantly call the pk via urls and pass them to the FBV.

Would this be an acceptable and “more” secure approach?

Example of my get portion of the CBV:

def get(self, request, RecordID):
    Record = get_object_or_404(RecordT, pk=RecordID)
    Stock = StockT.objects.filter(RecordID=Record)
    Record_form = RecordForm(instance=Record)
    Stock_form = AddStockForm()

    context = {
        'Record': Record,
        'Record_form': Record_form,
        'Stock': Stock,
        'Stock_form': Stock_form,
    }
    return render(request, 'Records/View_Record.html', context)

def post(self, request, RecordID):
    Record = get_object_or_404(RecordT, pk=RecordID)
    action = request.POST.get('action', '')

    action_map = {
        'Process_Record_To_Invoice': 'Process_Invoice',
    }

    if action in action_map:
        return redirect(reverse(f'Record:{action_map[action]}', kwargs={'RecordID': RecordID}))
    
    elif 'Record_form_submit' in request.POST:
        Record_form = RecordForm(request.POST, instance=Record)
        if Record_form.is_valid():
            Record_form.save()
            messages.success(request, "Record Updated")
        else:
            messages.error(request, "Record form has errors")
        return redirect(reverse('Records:View_Record', kwargs={'RecordID': RecordID}))

    elif 'Stock_form_submit' in request.POST:
        Stock_form = AddStockForm(request.POST)
        if Stock_form.is_valid():
            new_stock = Stock_form.save(commit=False)
            new_stock.RecordID = Record
            new_stock.save()
            messages.success(request, "Stock Added")
        else:
            context = {
                'Record': Record,
                'Record_form': RecordForm(instance=Record),
                'Stock': StockT.objects.filter(RecordID=Record),
                'Stock_form': Stock_form,  # Include the form with errors
            }
            return render(request, 'Records/View_Record.html', context)
        
    else:
        messages.error(request, "Invalid form submission")
        return redirect(reverse('Records:View_Record', kwargs={'RecordID': RecordID}))

Nope. There are probably as many people advocating for FBVs as there are for CBVs. There are also people who like the concept of CBVs but find the Django-provided implementation to be too “heavy” (See Django vanilla views as an alternative implementation.) Then there are people (like me) who believe the Django-provided CBVs are “good enough” as a base, but have done a fair amount of work to extend them for their own purposes.

Addressing questions slightly out of order -

Security is not an issue here at all. The structure of your views simply do not affect this outside the context of what’s being done by the view. The idea of 1 view or 10, or FBV vs CBV simply has no relevance regarding security. It is not an issue that needs or warrants to be taken into account when making this type of structural decision.

<opinion>
I believe all the “if” statements in your post handler to be unnecessary. That’s why I would have the action attribute for each form set to a different URL - one view per form, then write a view for each.

You can look at it this way. Either you write if statements to control the flow of events, or you let Django control that flow by having different URLs direct you to different views.

I’m on the side of letting Django do as much work as it can. I’d have one view doing the “GET” - it would prepare the page and render it. I would then have separate views for each “POST”, each one to specifically handle a single form. All of those forms would then redirect back to the “GET” view upon a successful submission.
</opinion>

Side note: When posting code here, enclose the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. This forces the forum software to keep your code properly formatted. (I’ve taken the liberty of modifying your post.)

1 Like

Thanks again @KenWhitesell for your time and effort. I really, really appreciate it.

You could check out iommi (https://docs.iommi.rocks/) which makes things like this very easy to make. It’s a much higher level system, where you don’t have to write templates, and you can compose forms and tables cleanly without needing to think about namespace collisions and stuff.

Full disclosure: I’m one of the authors.