pallets-eco / flask-classful Goto Github PK
View Code? Open in Web Editor NEWClass based views for Flask
Home Page: https://flask-classful.readthedocs.io
License: BSD 3-Clause "New" or "Revised" License
Class based views for Flask
Home Page: https://flask-classful.readthedocs.io
License: BSD 3-Clause "New" or "Revised" License
@hoatle - I think adding some small docs and standard OSS repo items would be good, such as a pull request template, contributor's guidelines, etc.
Let me know your thoughts and if you have something you want to do for this in mind, otherwise I can slowly get it done.
from Sungsoo Kim
"
Dear Hoat Le,
I would like to appreciate your great work for Flask-Classful. It is very very helpful for my flask project.
While I look-up the source code of Flask-Classful, I have a question why Flask-Classful does not create singleton instance. Actually it creates instances for every methods while making routing rules, I think it makes some overhead and prevent each method from sharing instance variables.
Currently In order to share instance variables among FlaskView's methods, we should use class variables instead of instance variables.
Do you have any reason or intention not to create singleton instance of FlaskView?"
Auto create docs/_build
if it does not exists
Hi, I have a 'BaseView' that extends FlaskView -- I want methods on this class to be ignored like they are for FlaskView -- is there a preferable way to implement this?
v0.14.0 is ready to be released
Flask-Classy
to Flask-Classful
flask_classy
to flask_classful
(note that this is a broken change, users must update module name from flask_classy
to flask_classful
)I'm having issues with flask-apispec using flask-classful. The issue is that apispec attaches information to the decorated function. I tried to fix it within their code but it is very complicated.
I fixed this in flask classful make_proxy_method to decorate the proxy rather than decorate the bound instance method. Anyone apposed to this fix? I'll submit a PR to aid the discussion.
If I remove 'int:' from the route string, it works as expected, but I want to specify parameter types as the same way flask's own route() does.
Doesn't flask-classful support argument type such as int:file_id? Or does it provide any other way to specify parameter types?
Sungsoo Kim
Traceback (most recent call last):
File "manage.py", line 62, in <module>
manager.run()
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/flask_script/__init__.py", line 412, in run
result = self.handle(sys.argv[0], sys.argv[1:])
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/flask_script/__init__.py", line 383, in handle
res = handle(*args, **config)
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/flask_script/commands.py", line 216, in __call__
return self.run(*args, **kwargs)
File "manage.py", line 30, in list_routes
url = url_for(rule.endpoint, **options)
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/flask/helpers.py", line 323, in url_for
force_external=external)
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/werkzeug/routing.py", line 1756, in build
rv = self._partial_build(endpoint, values, method, append_unknown)
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/werkzeug/routing.py", line 1671, in _partial_build
append_unknown)
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/werkzeug/routing.py", line 1679, in _partial_build
rv = rule.build(values, append_unknown)
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/werkzeug/routing.py", line 798, in build
add(self._converters[data].to_url(values[data]))
File "/Users/sungsoo/.pyenv/versions/3.5.2/envs/cms/lib/python3.5/site-packages/werkzeug/routing.py", line 1016, in to_url
value = self.num_convert(value)
ValueError: invalid literal for int() with base 10: '[queue_id]'
It's not possible to register different methods in different subdomains due to next code in
FlaskView.register:
if not subdomain and sub:
subdomain = sub
...
app.add_url_rule(rule, endpoint, proxy, subdomain=subdomain, **options)
its hard to implement the post function without running app example.
why post function should required view function? when I just have post with redirect in flask route decorator and work perfectly. please improve the documentation for flask newbie like. :(
I want a trailing slash for routes with parameters, such as /api/v1/templates/1/
. any way to achieve that? it gives a 404 right now and only works without the trailing slash
$ make check-style
to see the violations.
hjc 6 hours ago
Couple things. First off, to handle a missing content type from representations we should either throw a specific exception or provide the usage of like a default key in the representations dictionary that is a catch-all function that can always be used. Alternatively, we should just return the data we were given. I don't like using the first key in the dictionary, because dictionary ordering isn't guaranteed until Py3.6, in earlier versions you'd get a random key every time and that sounds SUPER frustrating.
from: https://github.com/teracyhq/flask-classful/pull/71/files#r141177560
so that we can write docs more easily
need to fix setup.py
v0.14.0 is going to be released soon, it's time for us to figure out what's next for flask-classful.
The first thing in my mind is to create more example, tutorials and integration examples, best practices for flask-classful to build robust REST APIs.
What's next? We need to figure this out together, there are some issues that we need to discuss to see if it's a fit for flask-classful (for example, IOC issue, etc).
I'm working on a rest api for a book database and have factored out two views: authors and books. The problem I'm having is that a basic GET for either an author or a book (http://localhost:5000/api/v1/[authors, books]/) works fine, but I'm getting a 404 error if I want to get a specfic author or book (http://localhost:5000/api/v1/author/1/ ).
[What the client sees]
$ curl -i http://localhost:5000/api/v1/authors/1/
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Server: Werkzeug/0.11.11 Python/2.7.11
Date: Sun, 20 Nov 2016 23:04:38 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
[Response from api]
127.0.0.1 - - [20/Nov/2016 18:04:38] "GET /api/v1/authors/1/ HTTP/1.1" 404 -
Here's what the relevant code for the AuthorsView class looks like:
class AuthorsView(FlaskView):
route_prefix = '/api/v1/'
def get(self, author_id=None):
if author_id:
author = Author.query.filter_by(author_id=author_id).first()
# if not author:
# raise InvalidUsage(
# 'Author with id of %i not found.' % id,
# status_code=404)
resp = jsonify(author.to_dict())
resp.status_code = 200
return resp
authors = [a.to_dict() for a in Author.query.all()]
#if not authors:
# raise InvalidUsage(
# 'Authors not found.' % id, status_code=404)
resp = jsonify(authors)
resp.status_code = 200
return resp
Any ideas what's going on here?
Is it possible to add support for flask-apispec decorators and docs generation?
for easier auto update
test code:
from flask import Flask
from flask_classful import FlaskView, route
# we'll make a list to hold some quotes for our app
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):
def before_request(self, name):
self.a = 1
def index(self):
return "<br>".join(quotes)
@route("/<name>", methods=["GET"])
def get_a(name):
return self.a
QuotesView.register(app)
if __name__ == '__main__':
app.run(port=1234)
curl
TypeError: before_request() got multiple values for argument 'name'
Hi, I was trying to combine flask-classful with webargs to make my API, this is an example code of how I did it, based on the quotes example in the docs:
from flask import Flask
from flask_classful import FlaskView
from webargs.flaskparser import use_args
from webargs import fields
# we'll make a list to hold some quotes for our app
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__)
app.config['DEBUG'] = True
put_args = {
"text": fields.Str(required=True)
}
class QuotesView(FlaskView):
base_args = ['args']
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
@use_args(put_args)
def put(self, args, id):
id = int(id)
if id >= len(quotes) - 1:
return "Not Found", 404
quotes[id] = args['text']
return quotes[id]
QuotesView.register(app)
if __name__ == '__main__':
app.run()
The interesting part of this is the use of the @use_args decorator, that adds an argument args
to the put function when called. To avoid having a /quotes/<args>/<id>/
URL (it should be /quotes/<id>/
) i set the base_args class attribute to ['args']
so that parameter is ignored from the URL. (BTW this isn't documented, i had to manually look the code to understand it). Until now everything works as expected.
The problem comes when I want to set the route_base atribute so it isn't automatically calculated from the class name:
# ...
class UglyNameClass(FlaskView):
base_args = ['args']
route_base = 'quotes'
# ...
@use_args(put_args)
def put(self, args, id):
id = int(id)
if id >= len(quotes) - 1:
return "Not Found", 404
quotes[id] = args['text']
return quotes[id]
UglyNameClass.register(app)
This should be routed to /quotes/<id>/
as the previous example, but it gets routed as /quotes/<args>/<id>/
, ignoring the base_args parameter I set.
Digging into the flask-restful code I saw that in get_route_base() the base_args is overriden when route_base exist:
@classmethod
def get_route_base(cls):
"""Returns the route base to use for the current class."""
if cls.route_base is not None:
route_base = cls.route_base
base_rule = parse_rule(route_base)
cls.base_args = [r[2] for r in base_rule] # THE PROBLEM IS HERE
else:
route_base = cls.default_route_base()
return route_base.strip("/")
I'm not sure why it overrides the value, but I think that it would be good to append to base_args instead of overriding it to support this use case.
It would be great to fix it to I could use views using webargs with a custom route_base
Hey, thanks for taking this over. I love this lib.
Wondering if there is a way to change how the routes are generated from function names.
class TestView(FlaskView):
def some_route(self):
pass
The above will take some_route
and turn it into test/someroute
. In my stuff i moved to hyphens because some long names get hard to read. Is there a way to change it to test/some-route
, other than a explicit @route
decorator? Some kind of default config to always change underscores to hyphens.
686be6e changes the order in which decorators are applied, due to the introduction of reversed()
.
This should be noted as a breaking change in the CHANGELOG?
Hello,
What is the way to inject dependencies into the views?
Is there a way I can have and pass stuff into __init__
, say a database handle or any other kind of service?
Thanks
Hi @hoatle, thank you for maintaining flask-classful!
After migration to Python3.7 I am being flooded with deprecation warning - could you have a look at it?
virtualenv/python3.7.1/lib/python3.7/site-packages/flask_classful.py:382: DeprecationWarning:
inspect.getargspec() is deprecated, use inspect.signature() or inspect.getfullargspec()
It might be that simple swapping of the order of this try-except would fix it:
https://github.com/teracyhq/flask-classful/blob/42fa5fce1fa97b1f00813b4ce0dd13ebebdb28cc/flask_classful.py#L446-L449
errors should be fixed:
Copying files from '_build/html/.' to '_deploy/develop'
Deploying on github pages now...
[gh-pages 4bd056b] docs updated at Tue Sep 12 02:22:31 UTC 2017
26 files changed, 11332 insertions(+), 239 deletions(-)
create mode 100644 develop/_sources/index.rst.txt
create mode 100644 develop/_sources/release.rst.txt
rewrite develop/_static/comment-bright.png (99%)
rewrite develop/_static/comment-close.png (99%)
rewrite develop/_static/comment.png (99%)
create mode 100644 develop/_static/jquery-3.1.0.js
rewrite develop/_static/jquery.js (98%)
rewrite develop/searchindex.js (95%)
remote: Invalid username or password.
fatal: Authentication failed for 'https://[secure]@github.com/teracyhq/flask-classful.git/'
make: *** [deploy_gh_pages] Error 128
from: https://travis-ci.org/teracyhq/flask-classful/jobs/274433983
The docs clearly states that you can pass to register any additional params that add_url_rule can accept.
The problem is that if y pass endpoint='home'
(a common param to add_url_rule) to the register function, then the following exception raises:
add_url_rule() got multiple values for argument 'endpoint'
That's because flask-classfull is not extracting from rule_options the posible user supplied values for the route_name (also known as 'endpoint').
Line 139 instead:
route_name = cls.build_route_name(name)
should be:
route_name = rule_options.get('endpoint') or cls.build_route_name(name)
or just simply change the register function signature to accept an endpoint parameter and then:
route_name = endpoint or cls.build_route_name(name)
I changed the original license to add Teracy copyright, however, I see that this is not the right way to do.
So let's revert and keep the original license instead.
(spoiler: this is already a feature, I just didn't read far enough into the docs)
From the docs:
Note
One important thing to note, is that
FlaskView
does not type your parameters, so if you want or need them you’ll need to define the route yourself using the@route
decorator.
It would be very neat to be able to do something like
class AnotherView(FlaskView):
route_base = "home"
def this_view(self, arg1: int, arg2: str):
return "Args: %s, %s" % (arg1, arg2,)
and end up with a route like /home/this_view/<int:arg1>/<string:arg2>
automatically, without having to do the @route
decorator by hand.
Flask understands these types ("converters"):
string
: accepts any text without a slash (the default)int
: accepts integersfloat
: like int but for floating point valuespath
: like the default but also accepts slashesany
: matches one of the items provideduuid
: accepts UUID stringsOf these, int
and float
will work as-is as Python type hints, str
can be interpreted as string
, and typing.Any
as any
.
The correct hint for path
and uuid
probably depends on what Flask passes them as – if Flask just passes them both as strings, then defining something like flask_classful.path
and flask_classful.uuid
(using typing.NewType
) to be used as type hints is probably the best approach. OTOH, if Flask actually passes a uuid.UUID
and/or pathlib.Path
/os.PathLike
, then it might be better to use those?
docs should be updated for all the changes
In previous versions (not sure of the range, because I don't know when this behavior was introduced) of Flask Classful, it was possible to have a public method in your view class and not have it automatically registered as a route. Take for example:
from flask import request
from flask_classful import FlaskView
class BaseView(FlaskView):
def copy_form_data(self):
return request.form.copy()
In this example, BaseView.copy_form_data
is registered as a route on all views that extend from BaseView
. IMHO this isn't desired behavior, but whatevs.
So another issue related to this that I discovered (and maybe related to #29) is that I cannot make BaseView.copy_form_data
into a static method because a flask_classful.DecoratorCompatibilityError
is raised when it's encountered.
I have found that the only way around this restriction is to make that method private, which isn't super optimal because it makes it tricky to document with Sphinx and that it breaks existing code that depends on it.
route
decorator, but it appears that the ship has sailed on that design decision.how can i achieve something like this
class MyBaseView(FlaskView): def __init__(self, model): self.model = model
cos i get error TypeError: init() missing 1 required positional argument: 'model'
after registering and running my app
we should add more tests to have 100% test coverage
If you use both route_base = "/"
and trailing_slash = False
, you'll get the following:
ValueError: urls must start with a leading slash
See Pull Request #30
This is very critical so that it's easier for everyone to upgrade.
Later on, for each pull request, we must make sure to update docs immediately before merging the pull requests.
Hello
is there a way to support this?
I have the following class that uses customized decorators token_req and webargs's use_kwargs:
class AdView(FlaskView):
@token_req
@use_kwargs(ad_schema.fields)
def post(self, current_user, kwargs):
print(**kwargs)
return construct.post(model=Ad, schema=ad_schema, data=kwargs, current_user=current_user)
The route I want is /ad
and the method is post, but because token_req
returns the current user and use_kwargs
returns the data entered, the url keeps showing 404 UNLESS I entered another two values like this:
/ads/3/fg
. So how to support such a system?
Hey, we have been using flask-classful for a while now and it has been awesome.
Really appreciate you taking this repo over!
Anyway, my question has to deal with architecting views for models who are children.
When i started the architecture it was my first flask app and i couldn't find any help so i made up a lame strat. At the time I was still using flask-classy, hadn't yet figured out it was taken over, and there have been several things that i thought were impossible until i thankfully replaced classy with classful so i am wondering if this question is similar.
With the code below, is there any way to make views "smart" so they know about their parent's rest routes without the repetition in route_prefix
. Now that i have 50+ models I would really like to be doing the better way for this architecture (if there is one), before i have 100+ models lol.
I have tried inheritance, and different combinations of route_prefix
with route_base
while following how they get built with the classful
internals and i couldn't find a good strategy.
class ParentView(FlaskView):
"""View for Parent model"""
route_prefix = '/api/v1/parent'
def get(self, parent_id: int):
pass
class ChildView(FlaskView):
"""View for Child model"""
route_prefix = '/api/v1/parent/<int:parent_id>/child'
def get(self, parent_id: int, child_id: int):
pass
class GrandKidView(FlaskView):
"""View for GrandKid model"""
route_prefix = '/api/v1/parent/<int:parent_id>/child/<int:child_id>/grandkids'
def get(self, parent_id: int, child_id: int, grandkid_id: int):
pass
As always thanks for any help!
for #14
this is best practice pallets/flask#1135
need to update docs
This works fine in Python 3.5:
class AwesomeView(FlaskView):
def patch(self, id):
return { "something" }
However if I want to use Python Type Hints, I have to manually add a @route
decorator:
class AwesomeView(FlaskView):
@route('/<id>', methods=['PATCH'])
def patch(self, id: str) -> dict:
return { "something" }
If I omit the decorator, then Flask-Classful triggers a ValueError
with the following stack trace:
File "/(...)/api/resources/posts.py", line 121, in <module>
AwesomeView.register(app)
File "/(...)/virtualenv/lib/python3.5/site-packages/flask_classful.py", line 134, in register
rule = cls.build_rule("/", value)
File "/(...)/virtualenv/lib/python3.5/site-packages/flask_classful.py", line 290, in build_rule
argspec = get_true_argspec(method)
File "/(...)/virtualenv/lib/python3.5/site-packages/flask_classful.py", line 360, in get_true_argspec
argspec = inspect.getargspec(method)
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py", line 1045, in getargspec
raise ValueError("Function has keyword-only arguments or annotations"
ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them
so that we could test many Python versions at once, it's easier for release and packaging for PyPi.
Hi,
First of all, many thanks for mantaining this project. I would like to know if this feature is currently supported. Imagine I have the following:
class ClassAView(FlaskView):
def init(self, pagename):
self.pagename = pagename
def index():
return 'Welcome to ' + self.pagename
Now, I would like to be able to instantiate an object of class ClassAView, register the Flask app to it, and give it a name as an argument to its constructor:
app = Flask(name)
instanceA = ClassAView('my page')
instanceA.register(app, route_base='/')
app.run()
I face some errors when doing something like this, so I tried to instantiate the object with an empty constructor, and then trying to define a setter method in order to provide "pagename". However, pagename is not stored and not displayed when requesting the index.
If not supported, I think it would be really nice to be able to interact with the REST facade with setter methods, or a constructor admitting arguments.
Many thanks for your help!
@hoavt please run and check documents in this project!
for PR: #16
Recently, we switched from Flask Classy to Flask Classful and discovered that all our routes using flask.jsonify with custom headers and status codes weren't working. For example:
def post(self):
return jsonify({"success": True}), 201, {"X-CUSTOM-HEADER": "1"}
would return the proper JSON response but would be missing the headers and status code. Currently, we've had to change all those routes to:def post(self):
return make_response(jsonify({"success": True}), 201, {"X-CUSTOM-HEADER": "1"})
This is a definite regression from Flask Classy and this PR remedies that.Shoutout to @hjc1710 for pair programming this with me.
contributors should be credited to a contributors file on the repo
I'm about to apply i18n into flask-classful-based project,
but I still didn't find the way of using Flask-babel with flask-classful.
how can i do this? does i18n feature can work with flask-classful?
use VIRTUAL_HOST: ${APP_DEV_VIRTUAL_HOST..}
instead of VIRTUAL_HOST: ${APP_PROD_VIRTUAL_HOST..}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.