Using django class based views (Beginner Level)

I am building a homepage that has a dynamically updated blogs section and a BMI calculator section. Initially, the page loads perfectly fine. But the problem is that when I calculate the BMI (POST request) the page refreshes and the blogs section (GET) disappears.

my urls.py

urlpatterns = [
    path('', HomeViews.as_view(), name='home_page'),
]

my views.py

class HomeViews(ListView, View):
    template_name="home_page.html"    
    
    def get(self, request, *args, **kwargs):
        posts_diet = BlogModel.objects.filter(cat__title='Healthy Diet').order_by('-created_at')[:1]
        return render(request, 'home_page.html', {
            'posts_diet' : posts_diet,
        })
        
    def post(self, request, *args, **kwargs):
        if request.method == "POST": 
            # get input
            height_metric = request.POST.get("height-metric")
            weight_metric = request.POST.get("weight-metric")
            #----bmi Formula-----
            if height_metric:
                bmi = float(weight_metric) / (float(height_metric)**2)
        else:
            bmi = 0

        return render(request, 'home_page.html', {
            'bmi': bmi,
            })

my home_page.html

<div>
  {% for pst in posts_diet %}
  <div>
    <img src="uploads/{{ pst.blog_main_image }}" />
    <div>
      <p>{{ pst.cat | striptags | slice:':50' }}</p>
      <p>{{ pst.created_at | date:"M d, Y" |striptags | safe }}</p>
    </div>
    <div>
      <h1>{{ pst.title | striptags | slice:':150' }}</h1>
      <button></button>
    </div>
  </div>
</div>
{% endfor %}

my terminal:

# initial homepage request (everything loads perfectly fine) 
[24/Dec/2021 14:34:21] "GET /static/css/dist/styles.css HTTP/1.1" 200 30992
[24/Dec/2021 14:34:21,606] - Broken pipe from ('127.0.0.1', 65275)

# on submitting POST request GET gets executed but GET content is not displayed
[24/Dec/2021 14:34:42] "GET / HTTP/1.1" 200 44233
[24/Dec/2021 14:34:42] "GET /static/css/dist/styles.css HTTP/1.1" 200 30992
[24/Dec/2021 14:35:41] "POST / HTTP/1.1" 200 37434

I think I can get away with this issue by using AJAX for form submission but that might not be a robust solution. Kindly advise …

First, this is redundant. ListView already inherits from View, you’re not gaining anything by supplying both here.

Then, by overriding both get and post, you’re overriding about 90% of the functionality of a generic class-based view.

Also, with overriding post, you don’t need to test request.method == "POST" - you wouldn’t be there otherwise.

Suggestion 1 - Take some time to understand how the generic CBVs work. Personally, I’ve found the Classy Class-Based Views docs and the CBV Diagrams page to be most useful for that.
You may want to either re-architect your view to work with the GCBV rather than around it.
Or, you might want to switch to a pure function-based view.

Absolutely is robust. But you probably want that submission to go to a different view. (And I would suggest a JSON submission and not a form submission - but that’s a personal preference on my part. Either one would work.)

Thanks for the help. I have restructured by views and now everything is working perfectly fine

my views.py

class HomeViews(View):   
    def post(self, request, *args, **kwargs):
        posts_diet = BlogModel.objects.filter(cat__title='Healthy Diet').order_by('-created_at')[:1]
        bmi = 0
        height_metric = request.POST.get("height-metric")
        weight_metric = request.POST.get("weight-metric")
        if height_metric:
            bmi = float(weight_metric) / (float(height_metric)**2)

        return render(request, 'home_page.html', {
            'bmi': bmi,
            'posts_diet' : posts_diet,
            })

previously I was doing it wrong by trying to execute both functions (GET & POST) within the View class by issuing only a “POST” request (through form submission)
however, no matter how many functions are there within the View class it only executes one single function for which you have sent the request.

So now I have squeezed in both my GET & POST views under one function so that when either is requested all code gets executed.

Do you see any problem with this solution?

Secondly, I am wondering for more complex pages where many GET/POST/PUT… etc have to be executed simultaneously and code inheritance is not an option how can one fit in all requests under one function?
One thing other than AJAX that comes to my mind is the Django {%include%} tag. In that case, we can have multiple views (through multiple HTML’s) but in that case, we will have to generate dummy URLs and that doesn’t seem to be a good idea.

I’d like to take a step back for a moment.

You’ve identified yourself as a “beginner level” with Django class-based views.

  • How would you describe your Django experience other than with the CBVs?

  • How would you describe your Python experience?

  • Are you coming to Django with experience in another web framework?

The reason I’m asking these questions is, quite honestly, the way you’re phrasing some of your questions or describing your situation leads me to believe that there are some aspects of how these work that you aren’t familiar with yet.

Let’s start with the basics of a CBV.

When you submit a request, Django creates a new instance of the CBV class. The as_view method returns the view method that call dispatch which calls a function matching the name of the request method. Having a method named post will not execute on a get. As written, a GET request issued to the HomeViews class is going to throw an error.

Briefly, you don’t. This is where I’m really not understanding where you’re coming from by saying this.

First, the methods don’t execute “simultaneously” under any circumstances. It’s not even the same instance of a class handling two different requests. Each HTTP request causes a new instance of the view class to be created.

This is another statement that’s not really making sense to me. It’s the combination of these two that are giving me an impression that you’re much more familiar with a different framework. I understand that you’re trying to draw parallels of what you know to Django, but unfortunately, those associations seem like they’re giving you some mistaken ideas of how to do things “the Django way”.

I will go so far as to say at this point, your goal is more views, not fewer. Strictly speaking, a view is a function that accepts a request and returns a response. If the nature of the request being received is different, or if the process of building the response is different, then that’s best served by a different view - not by overloading the functionality of an existing view.

If you have similar patterns of your requests and responses, then code inheritance through the use of class-based views can help. For example, you can think of the Django-provided generic class-based views as being the framework for a single-model BREAD application. (BREAD = “Browse”, “Read”, “Edit”, “Add”, “Delete”) The set of ListView, DetailView, UpdateView, CreateView, and DeleteView provide pretty much everything you need to accomplish that. You can actually stand up a complete application for that purpose without directly writing any Python code. (And note that you would have 5 different views for that application.)

1 Like