pallets-eco / flask-classful

Class based views for Flask
https://flask-classful.readthedocs.io
BSD 3-Clause "New" or "Revised" License
234 stars 55 forks source link

Why is constructor of FlaskView called per number of methods? #114

Open kidapu opened 5 years ago

kidapu commented 5 years ago

When I run the following code in python 3.6.9, The __init__() method is called 5 times.

from flask import Flask
from flask_classful import FlaskView, route

app = Flask(__name__)

class AppRouting(FlaskView):
    route_base = '/'

    def __init__(self):
        print("--------constructor---------")
        return 

    def test1(self):
        return "test1"

    def test2(self):    
        return "test2"

    def test3(self):
        return "test3"

    def test4(self):
        return "test4"

    def test5(self):
        return "test5"

if __name__ == '__main__':
    AppRouting.register(app)
    app.run()

Cmdline output is below.

$ python test.py 
--------constructor---------
--------constructor---------
--------constructor---------
--------constructor---------
--------constructor---------
 * Serving Flask app "F003-server2" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Is this right? I think the constructor method is called per number of methods. If possible, I want the constructor to be called only once.

hoatle commented 5 years ago

I'm not sure why. Maybe you can try to create one instance for all make_proxy_method call to see if everything works (at least all unit tests should be passed): https://github.com/teracyhq/flask-classful/blob/develop/flask_classful.py#L257-L260

kidapu commented 5 years ago

@hoatle I have two points to consider.

1)

I edited flask_classful.py linke below, and run with above code.

if init_argument is None:
    print("AAA")
    i = cls()
else:
    print("BBB")
    i = cls(init_argument)

The output is below.

AAA
--------constructor---------
AAA
--------constructor---------
AAA
--------constructor---------
AAA
--------constructor---------
AAA
--------constructor---------
 * Serving Flask app "F003-server2" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

2)

I installed Flask_Classful = 0.14.2. It's not the same with the latest code in GitHub.

dillibabukadati commented 3 years ago

I found one solution for it, when declare the class methods names with prefix of __ then init method is not being called for the method count. ex:

class AppRouting(FlaskView): route_base = '/'

def __init__(self):
    print("--------constructor---------")
    return 

def test1(self):
    return "test1"

def test2(self):    
    return "test2"

in above case init method will be called for 3 times. due to 2 methods declared under the class. now change it like below

class AppRouting(FlaskView): route_base = '/'

def __init__(self):
    print("--------constructor---------")
    return 

def __test1(self):
    return "test1"

def __test2(self):  
    return "test2"

now init method will get called only 1 time. But __test1 method can't be accessed in other class as it is declared as private. Unable to find any other resolution other than this.

Jiaoma commented 2 years ago

I met the same problem. And my solution is still ugly but needn't rename all the methods' names.

from flask import Flask
from flask_classful import FlaskView, route
from random import choice

quotes = [
    "A noble spirit embiggens the smallest man! ~ Jebediah Springfield",
    "If there is a way to do it better... find it. ~ Thomas Edison",
    "No one knows what he can do till he tries. ~ Publilius Syrus"
]

app = Flask(__name__)

class QuotesView(FlaskView):
    route_base = '/'

    @classmethod
    def _initilization(cls):
        print('Enter into init')
        # super().__init__()
        quotes.append("Never fade away. ~ V")
        print('Leave init')

    def index(self):
        return "<br>".join(quotes)

    def _get(self, id):
        id = int(id)
        if id < len(quotes) - 1:
            return quotes[id]
        else:
            return "Not Found", 404

    @route('/word_bacon/') #<--- Adding route
    def random(self):
        return choice(quotes)

    def add(self,a,b):
        return str(int(a)+int(b))

QuotesView._initilization()
QuotesView.register(app)

if __name__ == '__main__':
    app.run()

The output is like this:

Enter into init
Leave init
 * Serving Flask app "test2" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [15/Dec/2021 23:18:14] "GET / HTTP/1.1" 200 -
anembac commented 9 months ago

Any progress on this? Makes it impossible to for example neatly use a getter/setter in the registered class as they'll be called for two different instances of the class.