-- Table: user
CREATE TABLE user (
id INTEGER NOT NULL,
name VARCHAR (80) NOT NULL,
username VARCHAR (80) NOT NULL,
email VARCHAR (120) NOT NULL,
password VARCHAR (200) NOT NULL,
profile VARCHAR (180),
PRIMARY KEY (
id
),
UNIQUE (
username
),
UNIQUE (
email
)
);
INSERT INTO user (id, name, username, email, password, profile) VALUES (3, 'admin', 'admin', 'cairocoders@gmail.com', X'243262243132244A4A757A326B696734342E6835706B4A43303533382E6D315A53687369393631505278526A4D5767373337746939526372314C3761', 'profile.jpg');
-- Table: post
CREATE TABLE post (
id INTEGER NOT NULL,
title VARCHAR (200) NOT NULL,
slug VARCHAR (200) NOT NULL,
body TEXT NOT NULL,
category VARCHAR (100) NOT NULL,
image VARCHAR (150) NOT NULL,
user_id INTEGER NOT NULL,
views INTEGER,
comments INTEGER,
feature VARCHAR NOT NULL,
date_pub DATETIME NOT NULL,
PRIMARY KEY (
id
),
UNIQUE (
title
),
UNIQUE (
slug
),
FOREIGN KEY (
user_id
)
REFERENCES user (id) ON DELETE CASCADE
);
-- Table: comments
CREATE TABLE comments (
id INTEGER NOT NULL,
name VARCHAR (200) NOT NULL,
email VARCHAR (200) NOT NULL,
message TEXT NOT NULL,
post_id INTEGER NOT NULL,
feature BOOLEAN NOT NULL,
date_pub DATETIME NOT NULL,
PRIMARY KEY (
id
),
FOREIGN KEY (
post_id
)
REFERENCES post (id) ON DELETE CASCADE,
CHECK (feature IN (0, 1) )
);
URL
http://127.0.0.1:5000/login
Username : admin
password : cairocoders
http://127.0.0.1:5000/signup
http://127.0.0.1:5000/admin
http://127.0.0.1:5000/addpost
http://127.0.0.1:5000/update/3
http://127.0.0.1:5000/comments/
http://127.0.0.1:5000/search?q=python
http://127.0.0.1:5000/news/7-essential-vs-code-extensions-for-python-developers-in-2021
pip install Flask-Bcrypt = https://pypi.org/project/Flask-Bcrypt/
pip install Flask-Login = https://pypi.org/project/Flask-Login/
pip install flask-msearch = https://pypi.org/project/flask-msearch/
pip install Flask-SQLAlchemy = https://pypi.org/project/Flask-SQLAlchemy/
#app.py
from flaskblog import app
if __name__ == "__main__":
app.run(debug=True)
flaskblog/__init__.py
#flaskblog/__init__.py from flask import Flask from flask_bcrypt import Bcrypt #pip install Flask-Bcrypt https://pypi.org/project/Flask-Bcrypt/ from flask_sqlalchemy import SQLAlchemy #pip install Flask-SQLAlchemy = https://pypi.org/project/Flask-SQLAlchemy/ from flask_login import LoginManager #pip install Flask-Login = https://pypi.org/project/Flask-Login/ from flask_msearch import Search #pip install flask-msearch = https://pypi.org/project/flask-msearch/ app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db' app.config['SECRET_KEY']='cairocoders-ednalan' app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True db = SQLAlchemy(app) bcrypt = Bcrypt(app) search = Search() search.init_app(app) login_manager = LoginManager(app) login_manager.login_view = "login" login_manager.login_message_category = "info" from flaskblog import routesflaskblog/routes.py
#flaskblog/routes.py
from flask import render_template, redirect, url_for, request, flash,current_app,abort
from flask_login import login_user, login_required,logout_user,current_user
from flaskblog import app, bcrypt,db,search
from .forms import SignUpForm,LoginForm,PostForm
from .models import User, Post,Comments
import os
import secrets
def save_photo(photo):
rand_hex = secrets.token_hex(10)
_, file_extention = os.path.splitext(photo.filename)
file_name = rand_hex + file_extention
file_path = os.path.join(current_app.root_path, 'static/images', file_name)
photo.save(file_path)
return file_name
@app.route('/')
def index():
posts = Post.query.order_by(Post.id.desc()).all()
return render_template('post/index.html', posts=posts)
@app.route('/news/<string:slug>', methods=['POST','GET'])
def news(slug):
post = Post.query.filter_by(slug=slug).first()
comment = Comments.query.filter_by(post_id=post.id).filter_by(feature=True).all()
post.views = post.views + 1
db.session.commit()
Thanks =""
if request.method =="POST":
post_id = post.id
name = request.form.get('name')
email = request.form.get('email')
message = request.form.get('message')
comment = Comments(name=name,email=email,message=message,post_id=post_id)
db.session.add(comment)
post.comments = post.comments + 1
db.session.commit()
flash('Your comment has been submited submitted will be published after aproval of admin', 'success')
return redirect(request.url)
return render_template('post/news-details.html', post=post, comment=comment, Thanks=Thanks)
@app.route('/search')
def search():
keyword = request.args.get('q')
posts = Post.query.msearch(keyword,fields=['title'],limit=6)
return render_template('post/search.html', posts=posts)
@app.route('/admin')
@login_required
def admin():
posts = Post.query.order_by(Post.id.desc()).all()
return render_template('admin/home.html',posts = posts)
@app.route('/comments/', methods=['POST','GET'])
def comments():
comments =Comments.query.order_by(Comments.id.desc()).all()
return render_template('admin/comment.html',comments=comments)
@app.route('/check/<int:id>', methods=['POST','GET'])
@login_required
def check(id):
comment = Comments.query.get_or_404(id)
if (comment.feature == True):
comment.feature = False
db.session.commit()
else:
comment.feature = True
db.session.commit()
return redirect(url_for('comments'))
return redirect(url_for('comments'))
@app.route('/addpost',methods=['POST','GET'])
@login_required
def addpost():
form = PostForm(request.form)
if request.method =="POST" and form.validate():
photo = save_photo(request.files.get('photo'))
post = Post(title=form.title.data, body=form.content.data,category=request.form.get('category'),image=photo,author=current_user)
db.session.add(post)
db.session.commit()
flash('Your post has been added ','success')
return redirect(url_for('admin'))
return render_template('admin/addpost.html', form=form)
@app.route('/update/<int:id>',methods=['POST','GET'])
@login_required
def update(id):
form = PostForm(request.form)
post = Post.query.get_or_404(id)
form.title.data = post.title
form.content.data = post.body
if request.method=='POST' and form.validate():
if request.files.get('photo'):
try:
os.unlink(os.path.join(current_app.root_path, 'static/images/'+ post.image))
post.image = save_photo(request.files.get('photo'))
except:
post.image = save_photo(request.files.get('photo'))
post.title = form.title.data
post.body = form.content.data
post.category = request.form.get('category')
flash('Post has been updated', 'success')
db.session.commit()
return redirect(url_for('admin'))
return render_template('admin/addpost.html', form=form, post=post)
@app.route('/delete/<int:id>')
@login_required
def delete(id):
post = Post.query.get_or_404(id)
try:
os.unlink(os.path.join(current_app.root_path,'static/images/'+ post.image))
db.session.delete(post)
except:
db.session.delete(post)
flash('Post has deleted ','success')
db.session.commit()
return redirect(url_for('admin'))
@app.route('/delcomment/<int:id>')
@login_required
def delcomment(id):
comment = Comments.query.get_or_404(id)
db.session.delete(comment)
db.session.commit()
flash('Comment has deleted ','success')
return redirect(url_for('admin'))
@app.route('/signup', methods=['POST','GET'])
def signup():
form = SignUpForm(request.form)
if request.method == 'POST' and form.validate():
hashed_password = bcrypt.generate_password_hash(form.password.data)
user = User(name=form.name.data,username=form.username.data, email=form.email.data,password=hashed_password)
db.session.add(user)
db.session.commit()
flash('Thanks for registering, you able to login now','success')
return redirect(url_for('login'))
return render_template('admin/signup.html', form=form)
@app.route('/login', methods=['POST','GET'])
def login(): #username : admin pass: cairocoders
if current_user.is_authenticated:
next = request.args.get('next')
return redirect(next or url_for('admin'))
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
user = User.query.filter_by(username=form.username.data).first()
if not user:
flash('This user not exists','warning')
return redirect(url_for('login'))
if user and bcrypt.check_password_hash(user.password, form.password.data):
login_user(user)
flash('Logged in successfully.','success')
next = request.args.get('next')
return redirect(next or url_for('admin'))
flash('Invalid password','danger')
return render_template('admin/login.html', form=form)
@app.route('/logout')
@login_required
def logout():
logout_user()
flash('you are logout','success')
return redirect(url_for('login'))
flaskblog/models.py
#flaskblog/models.py
from flaskblog import db, login_manager
from datetime import datetime
from flask_login import UserMixin
from sqlalchemy import event
from slugify import slugify
@login_manager.user_loader
def load_user(user_id):
return User.query.filter_by(id=user_id).first()
class User(db.Model,UserMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
profile = db.Column(db.String(180), default="profile.jpg")
def __repr__(self):
return '<User %r>' % self.username
class Post(db.Model):
__searchable__ = ['title', 'body']
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), unique=True, nullable=False)
slug = db.Column(db.String(200), unique=True, nullable=False)
body = db.Column(db.Text, nullable=False)
category = db.Column(db.String(100), nullable=False)
image = db.Column(db.String(150), nullable=False, default='no-image.jpg')
user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
author = db.relationship('User', backref=db.backref('posts',lazy=True, passive_deletes=True))
views = db.Column(db.Integer,default=0)
comments = db.Column(db.Integer,default=0)
feature = db.Column(db.String, default=1, nullable=False)
date_pub = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
def __repr__(self):
return '<Post %r' % self.title
@staticmethod
def generate_slug(target, value, oldvalue, initiator):
if value and (not target.slug or value != oldvalue):
target.slug = slugify(value)
db.event.listen(Post.title, 'set',Post.generate_slug, retval=False)
class Comments(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), unique=False, nullable=False)
email = db.Column(db.String(200), unique=False, nullable=False)
message = db.Column(db.Text, nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('post.id', ondelete='CASCADE'), nullable=False)
post = db.relationship('Post', backref=db.backref('posts',lazy=True, passive_deletes=True))
feature = db.Column(db.Boolean, default=False, nullable=False)
date_pub = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
def __repr__(self):
return '<Post %r' % self.name
flaskblog/forms.py
#flaskblog/forms.py
from wtforms import Form, BooleanField, StringField,FileField, PasswordField, validators, TextAreaField,ValidationError
from flask_wtf.file import FileField, FileAllowed, FileRequired
class SignUpForm(Form):
name = StringField('Name', [validators.Length(min=2, max=25)])
username = StringField('Username', [validators.Length(min=4, max=25)])
email = StringField('Email Address', [validators.Length(min=6, max=35)])
password = PasswordField('Password', [validators.DataRequired(),
validators.EqualTo('confirm', message='Passwords must match')])
confirm = PasswordField('Repeat Password')
class LoginForm(Form):
username = StringField('Username', [validators.Length(min=4, max=25)])
password = PasswordField('Password',[validators.DataRequired()])
class PostForm(Form):
title = StringField('Title',[validators.DataRequired()])
content = TextAreaField('Content', [validators.DataRequired()])
photo = FileField()
flaskblog/templates/post/base.html
//flaskblog/templates/post/base.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png')}}" type="image/png">
<title>Python Flask Blog with Admin using flask_sqlalchemy, flask_login, flask_bcrypt and flask_msearch</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/bootstrap.css')}}">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css">
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}">
<link rel="stylesheet" href="{{url_for('static', filename='css/responsive.css')}}">
</head>
<body>
<header class="header_area">
<div class="logo_part">
<div class="container">
<div class="float-left">
<a class="logo" href="#"><img src="{{ url_for('static', filename='images/logo.png')}}" alt=""></a>
</div>
</div>
</div>
<div class="main_menu">
<nav class="navbar navbar-expand-lg navbar-light">
<div class="container">
<div class="container_inner">
<div class="collapse navbar-collapse offset" id="navbarSupportedContent">
<ul class="nav navbar-nav menu_nav">
<li class="nav-item active"><a class="nav-link" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link" href="#">About</a></li>
<li class="nav-item"><a class="nav-link" href="#">Contact</a></li>
</ul>
<ul class="nav navbar-nav navbar-right ml-auto">
<form action="{{ url_for('search')}}" method="GET" class="input-group mr-2">
<input type="text" class="form-control" name="q">
<div class="input-group-append">
<input type="submit" class="btn btn-outline-info" value="Search">
</div>
</form>
</ul>
</div>
</div>
</div>
</nav>
</div>
</header>
{% block content %}
{% endblock content %}
<footer class="footer-area">
<div class="container">
<p>
Copyright ©<script>
document.write(new Date().getFullYear());
</script> All rights reserved
</p>
</div>
</div>
</footer>
<script src="{{url_for('static', filename='js/jquery-3.2.1.min.js')}}"></script>
<script src="{{url_for('static', filename='js/popper.js')}}"></script>
<script src="{{url_for('static', filename='js/bootstrap.min.js')}}"></script>
</body>
</html>
flaskblog/templates/post/index.html
#flaskblog/templates/post/index.html
{% extends 'post/base.html' %}
{% block content %}
{% include 'post/slider.html'%}
<br>
<section class="news_area">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="main_title2">
<h2>Latest Blog</h2>
</div>
<div class="latest_news">
{% for post in posts %}
<div class="media">
<div class="d-flex">
<img class="img-fluid" src="{{url_for('static',filename='images/' + post.image)}}"
style="width:200px; height:200px;">
</div>
<div class="media-body">
<div class="choice_text">
<div class="date">
<a class="" href="#">{{post.author.name}}</a>
<a href="#"><i class="fa fa-calendar" aria-hidden="true"></i>March 14, 2018</a>
<a href="#"><i class="fa fa-comments-o" aria-hidden="true"></i> Views
{{post.views }} | {{post.comments}} Comennts</a>
</div>
<a href="{{url_for('news', slug=post.slug)}}">
<h4>{{post.title}}</h4>
</a>
<p>{{post.body |truncate(200, True) }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
{% endblock content %}
flaskblog/templates/post/news-details.html
#flaskblog/templates/post/news-details.html
{% extends 'post/base.html' %}
{% block content %}
<section class="news_area single-post-area p_100">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="main_blog_details">
<img class="img-fluid" src="{{url_for('static',filename='images/' + post.image)}}" alt="" style="width:100%; height: 400px;">
<h4> {{post.title}} </h4>
<div class="user_details">
<div class="float-left">
<a href="#">Python</a>
<a href="#">Flask</a>
</div>
<div class="float-right">
<div class="media">
<div class="media-body">
<h5>Cairocoder</h5>
<p>12 Dec, 2020 11:21 am</p>
</div>
</div>
</div>
</div>
<p>{{post.body}}</p>
<div class="news_d_footer">
<a href="#"><i class="lnr lnr lnr-heart"></i>cairocoders and 4 people like this</a>
<a class="justify-content-center ml-auto" href="#"><i class="lnr lnr lnr-bubble"></i> {{post.views }} Views | {{post.comments}} Comments</a>
<div class="news_socail ml-auto">
<a href="#"><i class="fa fa-facebook"></i></a>
<a href="#"><i class="fa fa-twitter"></i></a>
<a href="#"><i class="fa fa-youtube-play"></i></a>
<a href="#"><i class="fa fa-pinterest"></i></a>
<a href="#"><i class="fa fa-rss"></i></a>
</div>
</div>
</div>
<div class="comments-area">
<h4>{{post.comments}} Comments</h4>
{% for message in comment %}
<div class="comment-list">
<div class="single-comment justify-content-between d-flex">
<div class="user justify-content-between d-flex">
<div class="thumb">
<img src="img/blog/c1.jpg" alt="">
</div>
<div class="desc">
<h5><a href="#">{{message.name}}</a></h5>
<p class="date">{{message.date_pub.strftime('%Y')}}</p>
<p class="comment">
{{message.message}}
</p>
</div>
</div>
<div class="reply-btn">
<!-- <a href="" class="btn-reply text-uppercase">reply</a> -->
</div>
</div>
</div>
{% endfor %}
</div>
<div class="comment-form">
{% include 'admin/messages.html' %}
<h4>Leave a Comment</h4>
<form method="POST">
<div class="form-group form-inline">
<div class="form-group col-lg-6 col-md-6 name">
<input type="text" name="name" class="form-control" id="name" placeholder="Enter Name" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Enter Name'">
</div>
<div class="form-group col-lg-6 col-md-6 email">
<input type="email" name="email" class="form-control" id="email" placeholder="Enter email address" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Enter email address'">
</div>
</div>
<div class="form-group">
<textarea name="message" class="form-control mb-10" rows="5" name="message" placeholder="Messege" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Messege'" required=""></textarea>
</div>
<button type="submit" class="primary-btn submit_btn">Post Comment</button>
</form>
</div>
</div>
<div class="col-lg-4">
Right Side
</div>
</div>
</div>
</section>
{% endblock content %}
flaskblog/post/search.html
//flaskblog/post/search.html
{% extends 'post/base.html' %}
{% block content %}
<section class="news_area single-post-area p_100">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="main_blog_details">
<div class="latest_news">
{% for post in posts %}
<div class="media">
<div class="d-flex">
<img class="img-fluid" src="{{url_for('static',filename='images/' + post.image)}}"
style="width:200px; height:200px;">
</div>
<div class="media-body">
<div class="choice_text">
<div class="date">
<a class="" href="#">{{post.author.name}}</a>
<a href="#"><i class="fa fa-calendar" aria-hidden="true"></i>March 14, 2020</a>
<a href="#"><i class="fa fa-comments-o" aria-hidden="true"></i> Views
{{post.views }} | {{post.comments}} Comennts</a>
</div>
<a href="{{url_for('news', slug=post.slug)}}">
<h4>{{post.title}}</h4>
</a>
<p>{{post.body |truncate(200, True) }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="col-lg-4">
Right
</div>
</div>
</div>
</section>
{% endblock content %}
flaskblog/templates/post/slider.html
//flaskblog/templates/post/slider.html
<section class="home_banner_area">
<div class="banner_inner d-flex align-items-center bg-light">
<div class="container">
<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators" style="position:absolute; bottom:0%;">
<li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
<li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
<li data-target="#carouselExampleIndicators" data-slide-to="2"></li>
<li data-target="#carouselExampleIndicators" data-slide-to="3"></li>
</ol>
<div class="carousel-inner">
{% for post in posts %}
{% if loop.index == 1 %}
<div class="carousel-item active">
{% else %}
<div class="carousel-item">
{% endif %}
<div class="background_image banner_inner" style="background-image:url('{{url_for('static', filename='images/'+ post.image)}}');">
<div class="banner_content text-center" style="position:absolute; right:25%; top: 50%;">
<div class="date">
<a href="#">Gadgets</a>
<a href="#"><i class="fa fa-calendar" aria-hidden="true"></i>{{post.date_pub.strftime('%B %Y %d')}}</a>
<a href="#"><i class="fa fa-comments-o" aria-hidden="true"></i>05</a>
</div>
<h4><a href="{{url_for('news', slug=post.slug)}}" class="text-white" style="text-shadow:5px 5px 10px black;">{{post.title}}</a> </h4>
<p style="text-shadow:5px 5px 10px black;">{{post.body| truncate(150, True)}}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
flaskblog/templates/admin/login.html
//flaskblog/templates/admin/login.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="icon" href="static/web.jpg">
<title> Login</title>
</head>
<body>
<h1 class="text-center"> Login now </h1>
{% from "admin/_formhelpers.html" import render_field %}
<div class="container">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
{% include 'admin/messages.html' %}
<form method="post">
<div>
{{ render_field(form.username, class="form-control") }}
{{ render_field(form.password, class="form-control") }}
</div>
<p><input type=submit value="Login" class="btn btn-info">
</form>
</div>
<div class="col-md-3"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
flaskblog/templates/admin/signup.html
//flaskblog/templates/admin/signup.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="icon" href="static/web.jpg">
<title> Sign Up</title>
</head>
<body>
<h1 class="text-center"> Sign up now </h1>
{% from "admin/_formhelpers.html" import render_field %}
<div class="container">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
{% include 'admin/messages.html' %}
<form method="post">
<div>
{{ render_field(form.name, class="form-control")}}
{{ render_field(form.username, class="form-control") }}
{{ render_field(form.email, class="form-control") }}
{{ render_field(form.password, class="form-control") }}
{{ render_field(form.confirm, class="form-control") }}
</div>
<p><input type=submit value="Register" class="btn btn-info">
</form>
</div>
<div class="col-md-3"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
flaskblog/templates/admin/home.html
//flaskblog/templates/admin/home.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title> Dashboard </title>
</head>
<body>
<h2 class="text-center">User Dashboard </h2>
{% include 'admin/messages.html' %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{{url_for('admin')}}">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="{{url_for('admin')}}">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('addpost')}}">Add post</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('comments')}}">Comments</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout')}}"aria-disabled="true">Logout</a>
</li>
</ul>
</div>
</nav>
<table class="table table-striped table-bordered table-sm">
<thead class="bg-dark text-white">
<th>Sr</th>
<th>Title</th>
<th>Category</th>
<th>Author</th>
<th>Date</th>
<th>Image</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
{% for post in posts %}
<tr>
<td>{{loop.index}}</td>
<td>{{post.title}}</td>
<td>{{post.category}}</td>
<td>{{post.author.name}}</td>
<td>{{post.date_pub.strftime('%Y %B %d')}}</td>
<td> <img src="{{url_for('static', filename='images/' + post.image)}}" alt="{{post.category}}" width="40"></td>
<td> <a href="update/{{ post.id}}" class="btn btn-sm btn-info">Edit</a> </td>
<td><button type="button" class="btn btn-danger btn-sm" data-toggle="modal" data-target="#del{{post.id}}">
delete
</button></td>
</tr>
<!-- The Modal -->
<div class="modal" id="del{{post.id}}">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title text-danger"> Are you do you want delete this post </h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
{{post.title}}
</div>
<div class="modal-footer">
<div class="mr-auto"> <a href="delete/{{ post.id}}" class="btn btn-danger btn-sm"> Delete </a></div>
<button type="button" class="btn btn-primary btn-sm" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
{% endfor %}
</tbody>
</table>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
flaskblog/templates/admin/comment.html
//flaskblog/templates/admin/comment.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.4.0.js">
</script>
<title> Dashboard-Comments </title>
</head>
<body>
<h2 class="text-center">Dashboard-Comments </h2>
{% include 'admin/messages.html' %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{{url_for('admin')}}">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="{{url_for('admin')}}">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('addpost')}}">Add post</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout')}}" aria-disabled="true">Logout</a>
</li>
</ul>
</div>
</nav>
<table class="table table-striped table-bordered table-sm">
<thead class="bg-dark text-white">
<th>Sr</th>
<th>Name</th>
<th>Comments</th>
<th>Post ID</th>
<th>Date</th>
<th>Status</th>
<td>Delete</td>
</thead>
<tbody>
{% for comment in comments %}
<tr>
<td>{{loop.index}}</td>
<td>{{comment.name}}</td>
<td>{{comment.message | truncate(100, True)}}</td>
<td>{{comment.post.id}}</td>
<!-- <td>{{comment.feature}}</td> -->
<td>{{comment.date_pub.strftime('%Y %B %d')}}
</td>
{% if comment.feature == False %}
<td>
<a href="{{ url_for('check', id=comment.id) }}" class="text-center text-danger"> Pandding </a>
</td>
{% else %}
<td>
<a href="{{ url_for('delcomment', id=comment.id) }}" class="text-success"> Aproved </a>
</td>
{% endif %}
<td>
<a href="{{ url_for('check', id=comment.id) }}" class="btn btn-sm btn-danger"> Delete </a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js">
</script>
</body>
</html>
flaskblog/templates/admin/messages.html
//flaskblog/templates/admin/messages.html
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class=flashes>
{% for category, message in messages %}
<div class=" alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
flaskblog/templates/admin/_formhelpers.html
//flaskblog/templates/admin/_formhelpers.html
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li class="text-danger">{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
