Skip to content

Implement HTTP Method Override Middleware #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

You can use the mvc pattern in your flask application using this extension.

This real world implementation `FLASK MVC` example: https://github.com/negrosdev/negros.dev

## Installation

Run the follow command to install `mvc_flask`:
Expand Down Expand Up @@ -48,7 +46,7 @@ def create_app():

Now, you can use `src` as default directory for prepare your application.

You structure should be look like this:
You structure should be look like this:

```text
app
Expand Down Expand Up @@ -150,7 +148,7 @@ user.update PATCH, PUT /api/v1/user/<id>

## Controller

Now that configure routes, the `home_controller.py` file must contain the `HomeController` class, registering the `action`, e.g:
Now that configure routes, the `home_controller.py` file must contain the `HomeController` class, registering the `action`, e.g:

```python
from flask import render_template
Expand All @@ -177,43 +175,49 @@ class HomeController:

The previous example describes the `hi(self)` will be called every times that the visitors access the controller.

## PUT / DELETE
## PUT / PATCH / DELETE ...

we know that the HTML form doesn't send payload to `action` with `put` or `delete` method as attribute of `form tag`. But,
the `FLASK MVC` does the work for you, everything you need is add the `{{ mvc_form }} tag in HTML template. Look:

the `FLASK MVC` does the work for you, everything you need is add the tag in HTML template. Look:

```python
# app/controllers/messages_controller.py

from flask import render_template, redirect, url_for, flash
from flask import render_template, redirect, url_for, flash, request

class MessagesController:
def edit(self, id):
return render_template("messages/edit.html")
message = Message.query.get(id)

return render_template("messages/edit.html", message=message)

def update(self, id):
message = Message.query.get(id)
message.title = request.form.get('title')

db.session.add(message)
db.session.commit()
flash('Message sent successfully!')

return redirect(url_for(".edit"))
```


```html
```jinja
<!-- app/views/messages/edit.html -->

{% block content %}
<form action="{{ url_for('messages.update', id=message.id) }}" method="POST">
<input type="hidden" name="_method" value="put">
<input type="text" name="title" id="title" value="Yeahh!">

<form action='{{ url_for("messages.update", id=1) }}' method="put">
<textarea name="message"></textarea>
<input type="submit" value="update" />
<input type="submit" value="send">
</form>

{{ mvc_form }}

{% endblock %}

```

The `<input type="hidden" name="_method" value="put">` is necessary to work sucessfully!

## Views

Flask use the `templates` directory by default to store `HTMLs` files. However, using the `mvc-flask` the default becomes `views`. You can use the `app/views` directory to stores templates.
Expand Down
10 changes: 7 additions & 3 deletions mvc_flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from importlib import import_module

from flask import Flask, render_template, request
from flask import Flask
from flask.blueprints import Blueprint
from mvc_flask import plugins

from .router import Router
from .middleware.http_method_override import (
HTTPMethodOverrideMiddleware,
CustomRequest,
)


class FlaskMVC:
Expand All @@ -17,9 +20,10 @@ def init_app(self, app: Flask = None, path="app"):
self.path = path

app.template_folder = "views"
app.request_class = CustomRequest
app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app)

self.register_blueprint(app)
plugins.register(app)

def register_blueprint(self, app: Flask):
# load routes defined from users
Expand Down
48 changes: 48 additions & 0 deletions mvc_flask/middleware/http_method_override.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from flask import Request
from werkzeug.formparser import parse_form_data


class HTTPMethodOverrideMiddleware:
allowed_methods = frozenset(
[
"GET",
"POST",
"DELETE",
"PUT",
"PATCH",
]
)
bodyless_methods = frozenset(["GET", "HEAD", "OPTIONS", "DELETE"])

def __init__(self, app, input_name="_method"):
self.app = app
self.input_name = input_name

def __call__(self, environ, start_response):
if environ["REQUEST_METHOD"].upper() == "POST":
stream, form, files = parse_form_data(environ)
method = (form.get(self.input_name) or "").upper()

if method in self.allowed_methods:
environ["wsgi._post_form"] = form
environ["wsgi._post_files"] = files
environ["REQUEST_METHOD"] = method

if method in self.bodyless_methods:
environ["CONTENT_LENGTH"] = "0"

return self.app(environ, start_response)


class CustomRequest(Request):
@property
def form(self):
if "wsgi._post_form" in self.environ:
return self.environ["wsgi._post_form"]
return super().form

@property
def files(self):
if "wsgi._post_files" in self.environ:
return self.environ["wsgi._post_files"]
return super().files
9 changes: 0 additions & 9 deletions mvc_flask/plugins/__init__.py

This file was deleted.

40 changes: 0 additions & 40 deletions mvc_flask/plugins/form.py

This file was deleted.

Loading