Implementing Flask application using Object Oriented Programming (OOP's) [PART-2]
Published: 2021-12-05 16:33:09

<a href="https://dev.to/nandamtejas/implementing-flask-application-using-object-oriented-programming-oops-5cb">Please click here to check my first part [part-1] of this blog</a>

In previous section we have discussed about 
* About web Framework
* About Flask framework
* HTTP methods used in Flask framework
* A minimal Flask application
* A new approach using OOP's with Flask Framework

Now we are going to discuss further deep going into the concept

In the previous part we have discussed about the Flask framework which can be worked using classes and object, but not quitely.
We have just created a `FlaskAppWrapper` class which takes the flask app and allows us to add the endpoints and routing.

The `FlaskAppWrapper` class make sure functionality of `add_endpoint` method which is used to routing the method with appropriate endpoint given as an arguments passed to it.

In this section we are going to discuss about

- Routing
- Dynamic Routing
- EndpointHandler class
- How EndpointHandler class helps in routing

### Routing
App routing is used to map the specific URL with the `associated function` that is intended to perform some task.
The associated function is called `handler` in our case.

The `add_endpoint` method add the new endpoint for a given `handler` should be a callable function executed when you invoke the url with a given endpoint should be callable type rather you pass any arguments to it or not uses `add_url_rule(<url_rule>, <endpoint>, <view_function>)` which we have discussed before in the previous section which is used to perform routing for flask application.

Here,
- `<url_rule>` is the endpoint
- `<endpoint>` is the endpoint name which rather is the name of the handler and
- `<view_function>` is the associated function, rather say handler which is executed when we call the URL with the registered endpoint.
 
If you pass any arguments to that callable handler, you have the `request.view_args` method which should be imported from the `flask` module which will will store the arguments.

### Dynamic routing


Here we use a concept of `Dynamic routing`. Flask provides the functionality of the dynamic routing.
It is the process of getting dynamic data(variable names) in the URL and then using it.
In Flask you can have use following converters to convert dynamic input from the URL.

1. string
2. int
3. float
4. path ( It is simple a string but also accepts shashles in the URL)
5. uuid

`Dynamic URL's` in Flask play an important role in the ability to create unique URL's that aren't hard-coded into our application.


### EndpointHandler class
To pass the arguments, we have to create another class name `EndpointHandler` which is responsible for executing the `handler` according to the endpoint.

```python
class EndpointHandler(object):

    def __init__(self, action):
        self.action = action 

    def __call__(self, *args, **kwargs):
        response = self.action(*args, **kwargs)
        return response
```

Let's save in `handler.py` file.

In the above code we have created `EndpointHandler` class where the instance of that class is callable. Callable in the sense that instance of the class act's similar to the function. The `__call__` method makes the instance callable and executes what inside the `__call__` method.

Here in the class `EndpointHandler` takes the `action` which will be the any function or any method from class. 

Let's test with any built-in function in python.
There are so many built-in function in python such as `print`, `list`, `len` etc..

Let's try with `print` as an argument of an instance of the class.

```python
handler = EndpointHandler(print)
handler("Hello world")
```

The output will be like this

```
C:\>python handler.py
Hello world
```

That means `print` function is an action of `handler` which is an instance of a class and it is callable and prints anything we pass as an arguments. That means that `handler` instance has the functionality of the `print` function and acts as the same function we passed into it.

We have to check whether it works on `user defined function`.

Let's take an example of the function called action shown below.

```python
def action(a,b):
    """
    This action is an example function which takes the two parameter a and b of Integer type and returns the sum of a and b
    """
    return a + b
```

Now Let's make an instance of the class name `handler` and pass the `action` function as arguments in class.

```python
handler = EndpointHandler(action)
result = handler(5, 6)
print(result)
```

You will get the output as the sum of `5` and `6` which is `11`. That means we have concluded that the `EndpointHandler` class is the callable class which takes the `associative function` which is stored in the `action` parameter, performs the same functionality with the `action`.

### How `EndpointHandler class` will help in `Routing` in `flask`?

We have the `EndpointHandler` class which we have discussed in the previous part of the blog is shown below, the only change is we are going to pass the endpoint of `EndpointHandler`

```python
class FlaskAppWrapper(object):
  
    def __init__(self, app, **configs):
        self.app = app
        self.configs(**configs)
  
    def configs(self, **configs):
        for config, value in configs:
            self.app.config[config.upper()] = value
  
    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None, methods=['GET'], *args, **kwargs):
        self.app.add_url_rule(endpoint, endpoint_name, EndpointHandler(handler), methods=methods, *args, **kwargs)
  
    def run(self, **kwargs):
        self.app.run(**kwargs)
```

There are some changes should be done in `EndpointHandler` class when you are working in `flask` shown below.

```python
from flask import request, make_response

class EndpointHandler(object):

    def __init__(self, action):
        self.action = action 

    def __call__(self, *args, **kwargs):
        response = self.action(*args, **request.view_args)
        return make_response(response)
```

where, 

- `request.view_args` A dict of view arguments that matched the request. If an exception happened when matching, this will be `None`.

- `make_response` Convert the return value from a view function to an instance of response_class.

Now, We have to test our `FlaskAppWrapper` with a new function called `action` and we are going to pass `name` argument to it

```python
def action(name):
    """
    This function takes `name` argument and returns `Hello name`.
    """
    return "Hello " + name
```

Now we are going to define the flask app and we are going to register the `action` function.

```python
flask_app = Flask(__name__)
app = FlaskAppWrapper(flask_app)

# register the action function to app

app.add_endpoint('/action/<string:name>', 'action', action)

if __name__ == '__main__':
    app.run(debug=True)
```

The whole code looks like

```python
from flask import Flask, request, make_response

class EndpointHandler(object):
    
    def __init__(self, action):
        self.action = action 

    def __call__(self, *args, **kwargs):
        response = self.action(*args, **request.view_args)
        return make_response(response)

class FlaskAppWrapper(object):
      
    def __init__(self, app, **configs):
        self.app = app
        self.configs(**configs)
  
    def configs(self, **configs):
        for config, value in configs:
            self.app.config[config.upper()] = value
  
    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None, methods=['GET'], *args, **kwargs):
        self.app.add_url_rule(endpoint, endpoint_name, EndpointHandler(handler), methods=methods, *args, **kwargs)
  
    def run(self, **kwargs):
        self.app.run(**kwargs)

def action(name):
    """
    This function takes `name` argument and returns `Hello <name>`.
    """
    return "Hello " + name

flask_app = Flask(__name__)
app = FlaskAppWrapper(flask_app)

# register the action function to app

app.add_endpoint('/action/<string:name>', 'action', action)

if __name__ == '__main__':
    app.run(debug=True)
```

The code above shown works like this

1. Firstly, I have imported some requirements from `Flask` framework.

2. We have created the `EndpointHandler` class which is responsible for managing to pass the extra arguments or parameters to be passed when an `action` is invoked.

3. Then we have worked with the `FlaskAppWrapper` class and we have discussed about it in the previous part and we made some changes which is menctioned in the above discussion.

4. To test the `FlaskAppWrapper` class we have defined the `action` function which takes the `name` as the parameter and returns the string as `Hello <name>`.

5. We have defined the `flask_app` as the instance of the `Flask` class and this `flask_app` instance is given to our `FlaskAppWrapper` class and this instance is stored in `app`.

6. We have registered the `action` function to the `app` instance using the `add_endpoint` method by giving the appropriate `endpoint` to the `action` function. In this case `/action/<string:name>` is the endpoint for the `action` function that means, when we invoked the Flask app url with the endpoint above executes the `action` function which we have registered and we are going to see in the output section.

7. We are going to run the file using `app.run()` method and we see the output

We save the above code as `example6.py` and run using the command

    C://> python example6.py

And then we are going to click on the url shown below.

![run_app](https://user-images.githubusercontent.com/65908099/144752285-9765dfd9-cd65-43ea-b7a2-9f55fc66625d.png)

We are heading over to http://127.0.0.1:5000/action/Tejas in any search engines (I'm using Google) and we get the output as shown below

![part2_flask_example2](https://user-images.githubusercontent.com/65908099/144752471-c8c37ddd-381e-4235-9380-333bce48b654.png)

We have observed that after heading to the url above, You get the output as `Hello Tejas` and `Tejas` is the name I have passed over the `action` function.

We will check with other name say `Subash`.
Head over to http://127.0.0.1:5000/action/Subash the output is as shown below

![part2_flask_example_3](https://user-images.githubusercontent.com/65908099/144753275-35f4550c-6d9d-4aa7-bff7-3777b31b1c59.png)

### A broad example

Now we will test our application by adding many actions to our `FlaskAppWrapper` class.

The whole code looks like

```python
from flask import Flask, request, make_response

class EndpointHandler(object):
    
    def __init__(self, action):
        self.action = action 

    def __call__(self, *args, **kwargs):
        response = self.action(*args, **request.view_args)
        return make_response(response)

class FlaskAppWrapper(object):
      
    def __init__(self, app, **configs):
        self.app = app
        self.configs(**configs)
  
    def configs(self, **configs):
        for config, value in configs:
            self.app.config[config.upper()] = value
  
    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None, methods=['GET'], *args, **kwargs):
        self.app.add_url_rule(endpoint, endpoint_name, EndpointHandler(handler), methods=methods, *args, **kwargs)
  
    def run(self, **kwargs):
        self.app.run(**kwargs)

def action1(name):
    """
    This function takes `name` argument and returns `Hello name`.
    """
    return "Hello " + name

def action2():
    """
    This function returns "Action2 invoked"
    """

    return "Action2 invoked"

def action3(number):
    """
    This function returns the cube of the number 
    """
    return "Cube of {0} is {1}".format(number, number**3)



flask_app = Flask(__name__)
app = FlaskAppWrapper(flask_app)

# register all action functions to app

app.add_endpoint('/action1/<string:name>', 'action1', action1)
app.add_endpoint('/action2/', 'action2', action2)
app.add_endpoint('/action3/<int:number>', 'action3', action3)

if __name__ == '__main__':
    app.run(debug=True)
```

From above code, we have registered the 3 action functions to our `app`.

- `action1` takes the `name` parameter from the endpoint and returns `Hello <name>`.

- `action2` returns `Action2 invoked`.

- `action3` takes the `number` parameter from the endpoint and returns the cube of the number.

After running the above code we get the output shown below

* Heading over http://127.0.0.1:5000/action1/Tejas We get the output as shown below

![1_1](https://user-images.githubusercontent.com/65908099/144831867-ae99cc93-a8db-43cd-a8dd-744c28e37b3c.png)

* Heading over http://127.0.0.1:5000/action2/ we get the output as

![1_2](https://user-images.githubusercontent.com/65908099/144832064-4aa4c719-b8a8-4640-9508-faa901951b93.png)

* Heading over http://127.0.0.1:5000/action3/10 we get the output as 

![1_3](https://user-images.githubusercontent.com/65908099/144832190-200fa6ae-ff5f-48e4-af68-e972efee2a87.png)

Same url, different endpoints and different functions are invoked and executed.

We are going to declare that any number of actions can be registered in our `app` and I conclude that the `FlaskAppWrapper` class works as good as the Flask application and this is the approach I have researched.

### Summary
We have discussed in this part about
1. Routing and Dynamic Routing in Flask
2. Handling the flask endpoint using `EndpointHandler` class
3. Discussed and made some changes in the `FlaskAppWrapper` class.
4. Tested the updated code with the concept of `Dynamic routing`.