Skip to content

Commit

Permalink
Merge pull request apiflask#356 from apiflask/prefer-add-url-rule
Browse files Browse the repository at this point in the history
  • Loading branch information
greyli authored Sep 4, 2022
2 parents 946b27f + 6f9f4c1 commit 0a0f148
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 55 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def update_pet(pet_id, data):
```

<details>
<summary>You can also use class-based views with <code>MethodView</code></summary>
<summary>You can also use class-based views based on <code>MethodView</code></summary>

```python
from apiflask import APIFlask, Schema, abort
Expand All @@ -134,17 +134,13 @@ class PetOut(Schema):
category = String()


# "app.route" is just a shortcut,
# you can also use "app.add_url_rule" directly
@app.route('/')
class Hello(MethodView):

# use HTTP method name as class method name
def get(self):
return {'message': 'Hello!'}


@app.route('/pets/<int:pet_id>')
class Pet(MethodView):

@app.output(PetOut)
Expand All @@ -163,6 +159,10 @@ class Pet(MethodView):
for attr, value in data.items():
pets[pet_id][attr] = value
return pets[pet_id]


app.add_url_rule('/', view_func=Hello.as_view('hello'))
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```
</details>

Expand Down
6 changes: 2 additions & 4 deletions docs/migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ from flask.views import MethodView

# ...

@app.route('/pets/<int:pet_id>', endpoint='pet')
class Pet(MethodView):

decorators = [doc(responses=[404])]
Expand All @@ -107,10 +106,9 @@ class Pet(MethodView):
@app.output(PetOut)
def patch(self, pet_id, data):
pass
```

APIFlask supports to use the `route` decorator on a `MethodView`-based view class as a
shortcut, but you can also use the `add_url_rule` method to register it for flexibility.
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```

The `View`-based view class is not supported, you can still use it but currently
APIFlask can't generate OpenAPI spec (and API documentation) for it.
Expand Down
62 changes: 21 additions & 41 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -1028,45 +1028,24 @@ from apiflask import APIFlask
app = APIFlask(__name__)


@app.route('/pets/<int:pet_id>', endpoint='pet')
class Pet(MethodView):

def get(self, pet_id):
return {'message': 'OK'}

def delete(self, pet_id):
return '', 204
```

When creating a view class, it needs to inherit from the `MethodView` class, since APIFlask
can only generate OpenAPI spec for `MethodView`-based view classes.:

```python
from flask.views import MethodView

@app.route('/pets/<int:pet_id>', endpoint='pet')
class Pet(MethodView):
# ...
```

APIFlask supports to use the `route` decorator on view classes as a shortcut for `add_url_rule`:

```python
@app.route('/pets/<int:pet_id>', endpoint='pet')
class Pet(MethodView):
# ...
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```

!!! tip

If the `endpoint` argument isn't provided, the class name will be used as
endpoint. You don't need to pass a `methods` argument, since Flask will handle
it for you.
When creating a view class, it needs to inherit from the `MethodView` class, since APIFlask
can only generate OpenAPI spec for `MethodView`-based view classes.

Now, you can define view methods for each HTTP method, use the (HTTP) method name as method name:

```python
@app.route('/pets/<int:pet_id>', endpoint='pet')
class Pet(MethodView):

def get(self, pet_id): # triggered by GET request
Expand All @@ -1083,25 +1062,18 @@ class Pet(MethodView):

def patch(self, pet_id): # triggered by PATCH request
return {'message': 'OK'}


app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```

With the example application above, when the user sends a *GET* request to
`/pets/<int:pet_id>`, the `get()` method of the `Pet` class will be called,
and so on for the others.

From [version 0.10.0](/changelog/#version-0100), you can also use the `add_url_rule` method to register
view classes:

```python
class Pet(MethodView):
# ...

app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```

You still don't need to set the `methods`, but you will need if you want to register multiple rules
for one view classes based on the methods, this can only be achieved with `add_url_rule`. For
example, the `post` method you created above normally has a different URL rule than the others:
Normally you don't need to specify the methods, unless you want to register
multiple rules for one single view classe. For example, register the `post` method
to a different URL rule than the others:

```python
class Pet(MethodView):
Expand All @@ -1112,11 +1084,12 @@ app.add_url_rule('/pets/<int:pet_id>', view_func=pet_view, methods=['GET', 'PUT'
app.add_url_rule('/pets', view_func=pet_view, methods=['POST'])
```

However, you may want to create separate classes for different URL rules.

When you use decorators like `@app.input`, `@app.output`, be sure to use it on method
instead of class:

```python hl_lines="4 5 9 10 11 15 16"
@app.route('/pets/<int:pet_id>', endpoint='pet')
```python
class Pet(MethodView):

@app.output(PetOut)
Expand All @@ -1134,14 +1107,16 @@ class Pet(MethodView):
@app.output(PetOut)
def patch(self, pet_id, data):
# ...


app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```

If you want to apply a decorator for all methods, instead of repeat yourself,
you can pass the decorator to the class attribute `decorators`, it accepts
a list of decorators:

```python hl_lines="4"
@app.route('/pets/<int:pet_id>', endpoint='pet')
```python hl_lines="3"
class Pet(MethodView):

decorators = [auth_required(auth), doc(responses=[404])]
Expand All @@ -1161,8 +1136,13 @@ class Pet(MethodView):
@app.output(PetOut)
def patch(self, pet_id, data):
# ...


app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
```

Read [Flask docs on class-based views](https://flask.palletsprojects.com/views/) for more information.


## Use `abort()` to return an error response

Expand Down
10 changes: 5 additions & 5 deletions examples/cbv/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,12 @@ class PetOut(Schema):
category = String()


# "app.route" is just a shortcut,
# you can also use "app.add_url_rule" directly
@app.route('/')
class Hello(MethodView):

def get(self):
return {'message': 'Hello!'}


@app.route('/pets/<int:pet_id>')
class Pet(MethodView):

@app.output(PetOut)
Expand Down Expand Up @@ -62,7 +58,6 @@ def delete(self, pet_id):
return ''


@app.route('/pets')
class Pets(MethodView):

@app.output(PetOut(many=True))
Expand All @@ -78,3 +73,8 @@ def post(self, data):
data['id'] = pet_id
pets.append(data)
return pets[pet_id]


app.add_url_rule('/', view_func=Hello.as_view('hello'))
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
app.add_url_rule('/pets', view_func=Pets.as_view('pets'))

0 comments on commit 0a0f148

Please sign in to comment.