Skip to content

Commit 039cc5a

Browse files
anuragunniwongbandbryanwong8hackmd-deploy
authored
Implement Article model, decorators, schemas and routes. (#32)
* Added Anurag 1:1 meeting * Meeting notes for 1:1 05-12-2020 * Updated frontend notes * Create 5-13-20-Jason-onboarding.md * 5-13-20-Jason-onboarding * Delete 5-13-20-Jason-onboarding.md * Update 5-13-20-Jason-onboarding.md * Update 5-13-20-Jason-onboarding.md * Updated folders to not break windows laptops * Added Storybook * Implement Article Model, CRUD Routes, Decorators and Schemas - Issue #7 * Create pull_request_template.md * Implement Article Model, CRUD Routes, Decorators and Schemas - Issue #7 * Removing extra line * Implement Article Model, CRUD Routes, Decorators and Schemas - Issue #7 * Made some minor updates Co-authored-by: wongband <[email protected]> Co-authored-by: Bryan Wong <[email protected]> Co-authored-by: HackMD <[email protected]>
1 parent 1d48b0f commit 039cc5a

13 files changed

+13806
-2248
lines changed

.github/pull_request_template.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
What did you implement/accomplish in this pull request?
2+
Give a summary of what you completed in the pull request.
3+
4+
What are the issues are you closing?
5+
#ISSUE_NUMBER

.storybook/main.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
stories: ['../src/**/*.stories.js'],
3+
addons: [
4+
'@storybook/preset-create-react-app',
5+
'@storybook/addon-actions',
6+
'@storybook/addon-links',
7+
],
8+
};

api/backend/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
migrate = Migrate(app, db)
2525
CORS(app, supports_credentials=True, resources={r"/*": {"origins": ["*"]}})
2626

27+
from ..backend.articles.routes import articles_bp
2728
from ..backend.authentication.routes import authentication_bp
2829
from ..backend.home.routes import home_bp
2930
from ..backend.meta.routes import meta_bp
3031
from ..backend.users.routes import user_bp
3132
from ..backend.tags.routes import tag_bp
3233

34+
app.register_blueprint(articles_bp)
3335
app.register_blueprint(authentication_bp)
3436
app.register_blueprint(home_bp)
3537
app.register_blueprint(meta_bp)

api/backend/articles/decorators.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from ..articles.schemas import article_form_schema
2+
from ..models import Article
3+
from flask import request
4+
from functools import wraps
5+
6+
7+
# Decorator to check if the article exists
8+
def article_exists(f):
9+
@wraps(f)
10+
def wrap(*args, **kwargs):
11+
article = Article.query.get(kwargs["article_id"])
12+
13+
if article:
14+
return f(*args, **kwargs)
15+
else:
16+
return {
17+
"message": "Article does not exist"
18+
}, 404
19+
20+
return wrap
21+
22+
23+
# Decorator to check if a article form data is valid
24+
def valid_article_form(f):
25+
@wraps(f)
26+
def wrap(*args, **kwargs):
27+
form_data = request.get_json()
28+
errors = article_form_schema.validate(form_data)
29+
30+
# If form data is not validated by the article_form_schema, then return a 500 error
31+
# else create the article and add it to the database
32+
if errors:
33+
return {
34+
"message": "Missing or sending incorrect data to create a article. Double check the JSON data that it has everything needed to create a classroom."
35+
}, 422
36+
else:
37+
return f(*args, **kwargs)
38+
39+
return wrap
40+
41+
42+
# Decorator to check if the Article table has articles before printing upto 15 articles
43+
def article_table_empty(f):
44+
@wraps(f)
45+
def wrap(*args, **kwargs):
46+
if Article.query.first():
47+
return f(*args, **kwargs)
48+
else:
49+
return {
50+
"message": "No articles present"
51+
}, 404
52+
53+
return wrap

api/backend/articles/routes.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from .. import api, db
2+
from ..articles.decorators import article_exists, valid_article_form, article_table_empty
3+
from ..articles.utils import create_article, update_article
4+
from ..articles.schemas import article_schema, articles_schema
5+
from ..models import Article
6+
from flask import Blueprint, request
7+
from flask_restful import Resource
8+
9+
# Blueprint for articles
10+
articles_bp = Blueprint("articles", __name__)
11+
12+
13+
# Class to handle information regarding the Article model
14+
class Articles(Resource):
15+
# Function to create an article
16+
@valid_article_form
17+
def post(self):
18+
form_data = request.get_json()
19+
article = create_article(form_data)
20+
21+
db.session.add(article)
22+
db.session.commit()
23+
24+
return {"message": "Successfully created Article"}, 201
25+
26+
# Function to display all the articles up to a limit of 15
27+
@article_table_empty
28+
def get(self):
29+
article = Article.query.limit(15)
30+
31+
return articles_schema.dump(article, default=str)
32+
33+
34+
class ArticleCRUD(Resource):
35+
36+
# Function to edit an article
37+
@valid_article_form
38+
@article_exists
39+
def put(self, article_id):
40+
article = Article.query.get(article_id)
41+
form_data = request.get_json()
42+
update_article(article, form_data)
43+
44+
db.session.commit()
45+
46+
return {"message": "Article successfully updated"}, 200
47+
48+
# Function to delete an article
49+
@article_exists
50+
def delete(self, article_id):
51+
article = Article.query.get(article_id)
52+
53+
db.session.delete(article)
54+
db.session.commit()
55+
56+
return {"message": "Article successfully deleted"}, 200
57+
58+
# Function to display an article
59+
@article_exists
60+
def get(self, article_id):
61+
article = Article.query.get(article_id)
62+
63+
return article_schema.dump(article)
64+
65+
# Creates the routes for the classes
66+
api.add_resource(Articles, "/api/articles")
67+
api.add_resource(ArticleCRUD, "/api/articles/<int:article_id>")

api/backend/articles/schemas.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from marshmallow import Schema, fields
2+
import toastedmarshmallow
3+
4+
5+
# This schema is used to display article data
6+
class ArticleFormSchema(Schema):
7+
title = fields.Str(required=True)
8+
cover_image = fields.Str(required=False)
9+
content = fields.Str(required=False)
10+
11+
12+
# This schema is used to display article data
13+
class ArticleSchema(Schema):
14+
id = fields.Int(required=True)
15+
16+
class Meta:
17+
# Fields to show when sending data
18+
fields = ("id",)
19+
ordered = True
20+
21+
22+
article_form_schema = ArticleFormSchema()
23+
article_schema = ArticleSchema()
24+
articles_schema = ArticleSchema(many=True)
25+
article_form_schema.jit = toastedmarshmallow.Jit
26+
article_schema.jit = toastedmarshmallow.Jit
27+
articles_schema.jit = toastedmarshmallow.Jit

api/backend/articles/utils.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from ..models import Article
2+
3+
4+
# Function to create an article
5+
def create_article(form_data):
6+
article = Article(title=form_data["title"],
7+
cover_image=form_data["cover_image"],
8+
content=form_data["content"]
9+
)
10+
11+
return article
12+
13+
14+
#Function to update an article
15+
def update_article(article, form_data):
16+
article.title = form_data["title"]
17+
article.cover_image = form_data["cover_image"]
18+
article.content = form_data["content"]
19+
20+
return

api/backend/models.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from api.backend import db
2+
import datetime
23

34

45
# A many to many relationship to keep track of tags and users
@@ -8,6 +9,19 @@
89
)
910

1011

12+
class Article(db.Model):
13+
id = db.Column(db.Integer, primary_key=True)
14+
title = db.Column(db.Text, nullable=False)
15+
cover_image = db.Column(db.Text, nullable=True)
16+
content = db.Column(db.Text, nullable=False)
17+
date_published = db.Column(db.DateTime, nullable=False, default=datetime.date.today)
18+
likes = db.Column(db.Integer, nullable=False, default=0)
19+
is_published = db.Column(db.Boolean, nullable=False, default=True)
20+
21+
def __repr__(self):
22+
return f"Article('{self.id}')"
23+
24+
1125
class Meta(db.Model):
1226
id = db.Column(db.Integer, primary_key=True)
1327
roles = db.Column(db.Text, nullable=True)
@@ -77,4 +91,5 @@ class Tag(db.Model):
7791
back_populates="tags")
7892

7993
def __repr__(self):
80-
return f"Tag('{self.id} , {self.name}')"
94+
return f"Tag('{self.id} , {self.name}')"
95+

0 commit comments

Comments
 (0)