Initial commit

This commit is contained in:
openSIRP
2022-09-07 17:12:49 +02:00
commit b19a1150ec
14 changed files with 506 additions and 0 deletions

162
.gitignore vendored Normal file
View File

@@ -0,0 +1,162 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

41
Controllers/Login.py Normal file
View File

@@ -0,0 +1,41 @@
import datetime
from flask import request, jsonify
from flask_restful import Resource, abort
from Models.User import User
from app import app, jwt
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager, current_user, create_refresh_token
# Register a callback function that takes whatever object is passed in as the
# identity when creating JWTs and converts it to a JSON serializable format.
@jwt.user_identity_loader
def user_identity_lookup(user):
return user.id
# Register a callback function that loads a user from your database whenever
# a protected route is accessed. This should return any python object on a
# successful lookup, or None if the lookup failed for any reason (for example
# if the user has been deleted from the database).
@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
return User.query.filter_by(id=identity).one_or_none()
class Login(Resource):
def get(self, ):
user = User.query.filter_by(email=request.json['email']).first_or_404()
if not user or not user.check_password(request.json['password']):
abort(401, message='Unauthorized')
access_token = create_access_token(identity=user)
refresh_token = create_refresh_token(identity=user)
return jsonify(access_token=access_token, refresh_token=refresh_token)
class Refresh(Resource):
@jwt_required(refresh=True)
def get(self, ):
identity = get_jwt_identity()
access_token = create_access_token(identity=identity)
return jsonify(access_token=access_token)

66
Controllers/Post.py Normal file
View File

@@ -0,0 +1,66 @@
from flask import request, jsonify
from Models.Post import Post
from Models.Tag import Tag
from Models.Schema import post_schema, posts_schema
from flask_restful import Resource, abort
from app import db
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager, current_user
class PostListResource(Resource):
@jwt_required()
def get(self):
posts = Post.query.all()
return posts_schema.dump(posts)
@jwt_required()
def post(self):
tags_array= []
for tag_id in request.json['tags']:
tags_array.append(Tag.query.filter_by(id=tag_id).first())
new_post = Post(
title=request.json['title'],
content=request.json['content'],
author_id=current_user.id,
author=current_user,
tags=tags_array
)
db.session.add(new_post)
db.session.commit()
return post_schema.dump(new_post)
class PostResource(Resource):
@jwt_required()
def get(self, post_id):
post = Post.query.get_or_404(post_id)
return post_schema.dump(post)
@jwt_required()
def put(self, post_id):
post = Post.query.get_or_404(post_id)
post.title = request.json['title']
post.content = request.json['content']
db.session.commit()
return post_schema.dump(post)
@jwt_required()
def patch(self, post_id):
post = Post.query.get_or_404(post_id)
if 'title' in request.json:
post.title = request.json['title']
if 'content' in request.json:
post.content = request.json['content']
db.session.commit()
return post_schema.dump(post)
@jwt_required()
def delete(self, post_id):
post = Post.query.get_or_404(post_id)
db.session.delete(post)
db.session.commit()
return '', 204

22
Controllers/Tag.py Normal file
View File

@@ -0,0 +1,22 @@
from flask import request
from Models.Schema import tag_schema, tags_schema
from Models.Tag import Tag
from flask_restful import Resource, abort
from app import db
from werkzeug.security import generate_password_hash
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager, current_user
class TagListResource(Resource):
@jwt_required()
def get(self):
tags = Tag.query.all()
return tags_schema.dump(tags)
@jwt_required()
def post(self):
new_tag = Tag(
name=request.json['name']
)
db.session.add(new_tag)
db.session.commit()
return tag_schema.dump(new_tag)

63
Controllers/User.py Normal file
View File

@@ -0,0 +1,63 @@
from flask import request
from Models.User import User
from Models.Schema import user_schema, users_schema
from flask_restful import Resource, abort
from app import db
from werkzeug.security import generate_password_hash
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager, current_user
class UserListResource(Resource):
@jwt_required()
def get(self):
users = User.query.all()
return users_schema.dump(users)
@jwt_required()
def post(self):
new_user = User(
name=request.json['name'],
email=request.json['email'],
password=generate_password_hash(request.json['password'])
)
db.session.add(new_user)
db.session.commit()
return user_schema.dump(new_user)
class UserResource(Resource):
@jwt_required()
def get(self, user_id):
user = User.query.get_or_404(user_id)
return user_schema.dump(post)
@jwt_required()
def put(self, user_id):
user = User.query.get_or_404(user_id)
user.name = request.json['name']
user.email = request.json['email']
user.password = generate_password_hash(request.json['password'])
db.session.commit()
return user_schema.dump(post)
@jwt_required()
def patch(self, user_id):
user = User.query.get_or_404(user_id)
if 'name' in request.json:
user.name = request.json['name']
if 'email' in request.json:
user.email = request.json['email']
if 'password' in request.json:
user.password = generate_password_hash(request.json['password'])
db.session.commit()
return user_schema.dump(post)
@jwt_required()
def delete(self, user_id):
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return '', 204

16
LICENSE Normal file
View File

@@ -0,0 +1,16 @@
MIT No Attribution
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

23
Models/Post.py Normal file
View File

@@ -0,0 +1,23 @@
from app import db, ma
from Models.User import User
from Models.Tag import Tag
tags_posts = db.Table('tags_posts_mapping',
db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'), primary_key=True),
db.Column('post_id', db.Integer, db.ForeignKey('post.id'), primary_key=True)
)
class Post(db.Model):
__tablename__ = "post"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50))
content = db.Column(db.String(255))
tags = db.relationship('Tag', secondary=tags_posts, lazy='subquery',
backref=db.backref('posts', lazy=True))
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
author = db.relationship("User", backref="posts")
def __repr__(self):
return '<Post %s>' % self.title

33
Models/Schema.py Normal file
View File

@@ -0,0 +1,33 @@
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
from Models.User import User
from Models.Post import Post
from Models.Tag import Tag
class UserSchema(SQLAlchemyAutoSchema):
class Meta:
model = User
exclude = ("password",)
include_fk = True
include_relationships = True
load_instance = True
class PostSchema(SQLAlchemyAutoSchema):
class Meta:
model= Post
include_fk = True
include_relationships = True
load_instance = True
class TagSchema(SQLAlchemyAutoSchema):
class Meta:
model= Tag
include_fk = True
include_relationships = True
load_instance = True
user_schema = UserSchema()
users_schema = UserSchema(many=True)
post_schema = PostSchema()
posts_schema = PostSchema(many=True)
tag_schema = TagSchema()
tags_schema = TagSchema(many=True)

12
Models/Tag.py Normal file
View File

@@ -0,0 +1,12 @@
from app import db, ma
class Tag(db.Model):
__tablename__ = "tags"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
def __repr__(self):
return '<Tag %s>' % self.name

16
Models/User.py Normal file
View File

@@ -0,0 +1,16 @@
from app import db, ma
from werkzeug.security import check_password_hash
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
email = db.Column(db.String(255))
password = db.Column(db.String(255))
def __repr__(self):
return '<User %s>' % self.name
def check_password(self, password):
return check_password_hash(self.password, password)

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# Python-Flask-Template
Template pour créer une API REST en Python avec Flask, SQLAlchemy, Marshmallow et JWT pour l'authentification

35
app.py Normal file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python
import datetime
import jwt
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_restful import Api, abort
from werkzeug.security import generate_password_hash, check_password_hash
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager
import config
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
ma = Marshmallow(app)
api = Api(app)
jwt = JWTManager(app)
from Controllers.Post import PostListResource, PostResource
from Controllers.User import UserListResource, UserResource
from Controllers.Tag import TagListResource
from Controllers.Login import Login, Refresh
api.add_resource(UserListResource, '/v1/user')
api.add_resource(UserResource, '/v1/user/<int:post_id>')
api.add_resource(Login, '/v1/login')
api.add_resource(Refresh, '/v1/refresh')
api.add_resource(PostListResource, '/v1/posts')
api.add_resource(PostResource, '/v1/posts/<int:post_id>')
api.add_resource(TagListResource, '/v1/tags')
db.create_all()
if __name__ == '__main__':
app.run()

7
config.py Normal file
View File

@@ -0,0 +1,7 @@
from datetime import timedelta
DEBUG = True
JWT_SECRET_KEY = "SECRET"
SQLALCHEMY_DATABASE_URI='sqlite:///test.db'
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=24)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)

7
requirements.txt Normal file
View File

@@ -0,0 +1,7 @@
Flask
Flask-SQLAlchemy
Flask-RESTful
flask-marshmallow
flask-jwt-extended
pyjwt
marshmallow-sqlalchemy