Flask Mini Blog Project

Flask Mini Blog Project

Let's build a mini blog application using Flask! This project will help you understand web development fundamentals while creating something practical and functional. By the end, you'll have a working blog where users can view posts, create new content, and interact with a database.

Setting Up Your Environment

Before we dive into coding, let's set up our development environment. You'll need Python installed on your system. Create a new directory for your project and set up a virtual environment:

python -m venv blogenv
source blogenv/bin/activate  # On Windows: blogenv\Scripts\activate
pip install flask flask-sqlalchemy

Now create a file called app.py - this will be our main application file. Let's start with a basic Flask structure:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Welcome to our Mini Blog!"

if __name__ == '__main__':
    app.run(debug=True)

Run your application with python app.py and visit http://127.0.0.1:5000 to see your basic Flask app working!

Database Setup with SQLAlchemy

For our blog, we need to store posts and potentially user data. We'll use SQLite with Flask-SQLAlchemy for database management. Add these imports and configurations to your app.py:

from flask_sqlalchemy import SQLAlchemy
import os

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

Now let's define our Post model. This will represent blog posts in our database:

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return f"Post('{self.title}', '{self.date_posted}')"

To create the database, open a Python shell in your project directory and run:

from app import db
db.create_all()
Database Operation Command Purpose
Create Database db.create_all() Initializes database tables
Add Record db.session.add(post) Adds new post to session
Commit Changes db.session.commit() Saves changes to database
Query All Post.query.all() Retrieves all posts

Creating Blog Routes

Now let's create routes for our blog. We'll need routes to view posts, create new posts, and handle individual post pages. Add these routes to your app.py:

from flask import render_template, request, redirect, url_for
from datetime import datetime

@app.route('/')
def home():
    posts = Post.query.order_by(Post.date_posted.desc()).all()
    return render_template('home.html', posts=posts)

@app.route('/post/<int:post_id>')
def post(post_id):
    post = Post.query.get_or_404(post_id)
    return render_template('post.html', post=post)

@app.route('/create', methods=['GET', 'POST'])
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        new_post = Post(title=title, content=content)
        db.session.add(new_post)
        db.session.commit()
        return redirect(url_for('home'))
    return render_template('create.html')

Templates and Frontend

Create a templates folder in your project directory. We'll need three HTML templates: base template, home page, post detail page, and create post page.

base.html (layout template):

<!DOCTYPE html>
<html>
<head>
    <title>Mini Blog</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <nav>
        <a href="{{ url_for('home') }}">Home</a>
        <a href="{{ url_for('create') }}">New Post</a>
    </nav>
    <div class="container">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

home.html:

{% extends "base.html" %}
{% block content %}
<h1>Blog Posts</h1>
{% for post in posts %}
<article>
    <h2>{{ post.title }}</h2>
    <p>{{ post.content[:150] }}...</p>
    <a href="{{ url_for('post', post_id=post.id) }}">Read More</a>
    <small>Posted on {{ post.date_posted.strftime('%Y-%m-%d') }}</small>
</article>
{% endfor %}
{% endblock %}

Let's also create a simple CSS file in a static folder to make our blog look better:

style.css:

body {
    font-family: Arial, sans-serif;
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

nav {
    background: #333;
    padding: 10px;
    margin-bottom: 20px;
}

nav a {
    color: white;
    margin-right: 15px;
    text-decoration: none;
}

article {
    border-bottom: 1px solid #ccc;
    padding: 20px 0;
}

form {
    display: flex;
    flex-direction: column;
    max-width: 600px;
}

input, textarea {
    margin-bottom: 15px;
    padding: 10px;
}

Adding User Authentication

Let's enhance our blog by adding user authentication. We'll create a User model and implement login/logout functionality:

from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user

login_manager = LoginManager()
login_manager.init_app(app)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy=True)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

Now modify the Post model to include author information:

class Post(db.Model):
    # ... existing fields ...
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

Essential steps for implementing authentication: - Create login and registration routes - Add user session management - Protect post creation route - Display user-specific content - Add logout functionality

Authentication Component Purpose Implementation
User Model Store user credentials SQLAlchemy with password hashing
Login Manager Handle user sessions Flask-Login extension
Protected Routes Restrict access to authenticated users @login_required decorator
Password Security Secure user passwords Werkzeug security functions

Adding Comments Functionality

Let's allow users to comment on posts. First, create a Comment model:

class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, default=datetime.utcnow)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    post = db.relationship('Post', backref=db.backref('comments', lazy=True))
    author = db.relationship('User', backref=db.backref('comments', lazy=True))

Add comment routes and modify your post template to display comments:

@app.route('/post/<int:post_id>/comment', methods=['POST'])
@login_required
def add_comment(post_id):
    post = Post.query.get_or_404(post_id)
    content = request.form['content']
    new_comment = Comment(content=content, post_id=post_id, user_id=current_user.id)
    db.session.add(new_comment)
    db.session.commit()
    return redirect(url_for('post', post_id=post_id))

Error Handling

Let's add proper error handling to make our application more robust:

@app.errorhandler(404)
def not_found_error(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500

Create corresponding error templates in your templates folder. This ensures users see friendly error messages instead of technical details.

Deployment Considerations

When you're ready to deploy your mini blog, here are some important considerations:

Production settings: Always set debug=False in production. Use environment variables for sensitive information like secret keys.

Database: Consider using PostgreSQL instead of SQLite for production applications. It handles concurrent connections better.

Static files: Configure your web server (like Nginx or Apache) to serve static files directly for better performance.

Security: Implement CSRF protection, use HTTPS, and regularly update your dependencies.

Key deployment steps: - Choose a hosting platform (Heroku, PythonAnywhere, or traditional VPS) - Set up production database - Configure environment variables - Set up proper logging - Implement backup strategies - Monitor application performance

Testing Your Application

Testing is crucial for maintaining a reliable application. Let's add some basic tests:

import unittest
from app import app, db

class BlogTestCase(unittest.TestCase):
    def setUp(self):
        app.config['TESTING'] = True
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
        self.app = app.test_client()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def test_home_page(self):
        response = self.app.get('/')
        self.assertEqual(response.status_code, 200)
        self.assertIn(b'Blog Posts', response.data)

Run your tests with python -m unittest discover to ensure everything works correctly.

Adding Advanced Features

Once you have the basic blog working, consider adding these advanced features:

Search functionality: Allow users to search through blog posts.

Categories and tags: Organize posts by topics.

Image uploads: Let users add images to their posts.

API endpoints: Create RESTful API for your blog content.

Social sharing: Add buttons to share posts on social media.

Email notifications: Notify users of new comments or posts.

Each of these features will help you learn different aspects of web development while making your blog more functional and professional.

Remember to back up your database regularly and keep your dependencies updated for security. Flask development is all about starting simple and gradually adding features as you learn. Happy coding!