Blog

  • Python Django Custom User Model Guide

    Creating a robust authentication system is one of the first challenges every Django developer faces. While Django’s default User model works for simple projects, real‑world applications often need extra fields, custom permissions, or a different login identifier such as an email address. That’s where a **custom user model** comes into play. In this guide we’ll walk through why you should replace the default model, how to design a custom user model with AbstractUser or AbstractBaseUser, and the exact steps to implement, test, and migrate it safely—all while keeping SEO‑friendly keywords like “Python Django custom user model” front and center.

    Why Replace Django’s Default User Model?

    Before diving into code, it’s important to understand the benefits of a custom user model. Replacing the default auth.User early in a project saves you from costly migrations later and gives you full control over authentication data.

    • Flexibility: Add fields such as phone_number, profile_picture, or date_of_birth without creating separate profile tables.
    • Custom login identifiers: Use email, mobile number, or a UUID as the primary credential instead of a username.
    • Better security: Implement custom password hashing, two‑factor authentication flags, or password‑expiry policies directly in the model.
    • Future‑proofing: As your app scales, you’ll already have a model that can evolve with new business requirements.

    Choosing the Right Base Class

    Django offers two abstract base classes for custom users:

    1. AbstractUser

    Extends the built‑in User model and retains the same fields (username, first_name, last_name, etc.) while allowing you to add or modify fields. This is the simplest path if you only need a few extra attributes.

    2. AbstractBaseUser

    Provides only the core authentication features (password handling, permissions) and leaves you to define every field, including the identifier used for login. Choose this when you want a completely different schema, such as email‑only login.

    Step‑by‑Step: Building a Custom User Model with AbstractUser

    Below is a practical example that adds date_of_birth and bio fields while switching the login identifier to email.

    # myapp/models.py
    from django.contrib.auth.models import AbstractUser
    from django.db import models
    from django.utils.translation import gettext_lazy as _
    
    class CustomUser(AbstractUser):
        email = models.EmailField(_('email address'), unique=True)
        date_of_birth = models.DateField(null=True, blank=True)
        bio = models.TextField(max_length=500, blank=True)
    
        USERNAME_FIELD = 'email'          # Use email to log in
        REQUIRED_FIELDS = ['username']   # Username is still required for admin
    
        def __str__(self):
            return self.email
    

    Key points to note:

    • USERNAME_FIELD tells Django which field to treat as the unique identifier.
    • REQUIRED_FIELDS lists fields that will be prompted for when creating a superuser via createsuperuser.
    • We keep username for backward compatibility with the admin site, but you can remove it entirely if you prefer.

    Step‑by‑Step: Building a Custom User Model with AbstractBaseUser

    If you want a lean user table that only stores email, password, and a few custom fields, follow this pattern:

    # myapp/models.py
    from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
    from django.contrib.auth.models import PermissionsMixin
    from django.db import models
    from django.utils import timezone
    
    class CustomUserManager(BaseUserManager):
        def create_user(self, email, password=None, **extra_fields):
            if not email:
                raise ValueError('The Email field must be set')
            email = self.normalize_email(email)
            user = self.model(email=email, **extra_fields)
            user.set_password(password)
            user.save(using=self._db)
            return user
    
        def create_superuser(self, email, password, **extra_fields):
            extra_fields.setdefault('is_staff', True)
            extra_fields.setdefault('is_superuser', True)
            return self.create_user(email, password, **extra_fields)
    
    class CustomUser(AbstractBaseUser, PermissionsMixin):
        email = models.EmailField(unique=True)
        first_name = models.CharField(max_length=30, blank=True)
        last_name = models.CharField(max_length=30, blank=True)
        date_joined = models.DateTimeField(default=timezone.now)
        is_active = models.BooleanField(default=True)
        is_staff = models.BooleanField(default=False)
    
        objects = CustomUserManager()
    
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = []   # Email & password are required by default
    
        def __str__(self):
            return self.email
    

    This approach gives you full control over the database schema and is ideal for API‑first projects where a username is unnecessary.

    Configuring Django to Use the Custom Model

    Once your model is ready, tell Django to use it globally by adding the following line to settings.py:

    # settings.py
    AUTH_USER_MODEL = 'myapp.CustomUser'
    

    Make sure myapp is listed before django.contrib.auth in INSTALLED_APPS if you plan to override admin templates later.

    Running Migrations Correctly

    Because the user model is a core part of Django’s auth system, you must create migrations before running any other app migrations. Follow this order:

    1. Run python manage.py makemigrations myapp to generate the custom user migration.
    2. Run python manage.py migrate to apply the migration. If you already have a database with the default auth_user table, consider creating a fresh database or using a data migration strategy to move existing users.

    Updating the Django Admin

    To manage your new user model from the admin site, register it with a custom UserAdmin class. This ensures the admin respects the new USERNAME_FIELD and displays your extra fields.

    # myapp/admin.py
    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin
    from .models import CustomUser
    
    class CustomUserAdmin(UserAdmin):
        model = CustomUser
        list_display = ('email', 'first_name', 'last_name', 'is_staff')
        list_filter = ('is_staff', 'is_superuser', 'is_active')
        ordering = ('email',)
        search_fields = ('email', 'first_name', 'last_name')
        fieldsets = (
            (None, {'fields': ('email', 'password')}),
            ('Personal info', {'fields': ('first_name', 'last_name', 'date_of_birth', 'bio')}),
            ('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
            ('Important dates', {'fields': ('last_login', 'date_joined')}),
        )
        add_fieldsets = (
            (None, {
                'classes': ('wide',),
                'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
            ),
        )
    
    admin.site.register(CustomUser, CustomUserAdmin)
    

    Common Pitfalls & How to Avoid Them

    • Changing the user model after migrations: Once you have run migrate, swapping AUTH_USER_MODEL is painful. Always decide on a custom model at project start.
    • Forgotten AUTH_USER_MODEL reference: Third‑party apps that reference auth.User directly will break. Use settings.AUTH_USER_MODEL in foreign keys: user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE).
    • Missing manager methods: When using AbstractBaseUser, you must provide create_user and create_superuser in the custom manager; otherwise createsuperuser will fail.
    • Admin password reset links: Ensure the admin’s password reset form uses the correct field name (email instead of username) by overriding the form if necessary.

    Testing Your Custom User Model

    Write unit tests to guarantee that authentication works as expected. Below is a minimal test case using Django’s TestCase:

    # myapp/tests.py
    from django.test import TestCase
    from django.contrib.auth import get_user_model
    
    class CustomUserModelTests(TestCase):
        def setUp(self):
            self.User = get_user_model()
    
        def test_create_user_with_email(self):
            user = self.User.objects.create_user(
                email='test@example.com',
                password='strongpassword123'
            )
            self.assertEqual(user.email, 'test@example.com')
            self.assertTrue(user.check_password('strongpassword123'))
    
        def test_superuser_flags(self):
            admin = self.User.objects.create_superuser(
                email='admin@example.com',
                password='adminpass'
            )
            self.assertTrue(admin.is_staff)
            self.assertTrue(admin.is_superuser)
    

    Running python manage

  • Python Django Rest Framework Drf Tutorial

    Python Django REST Framework (DRF) tutorial – If you’re a Python developer looking to build robust, scalable APIs quickly, Django REST Framework (DRF) is the tool you’ve been waiting for. In this comprehensive guide we’ll walk you through everything from setting up a fresh Django project to mastering serializers, viewsets, routers, authentication, pagination, testing, and deployment. By the end of this tutorial you’ll have a production‑ready RESTful API that you can extend for any web or mobile application.

    Why Choose Django REST Framework?

    DRF sits on top of Django, leveraging its mature ORM, authentication system, and admin interface while adding powerful API‑specific features. Here are a few reasons why DRF is a top choice for Python developers:

    • Browsable API: Instantly explore your endpoints in a web browser.
    • Serialization: Convert complex Django models to JSON, XML, or other formats with minimal code.
    • Viewsets & Routers: Reduce boilerplate by automatically generating CRUD routes.
    • Authentication & Permissions: Built‑in support for token, session, OAuth, and custom schemes.
    • Extensible: Plug‑in pagination, throttling, and custom renderers as needed.

    Getting Started: Project Setup

    1. Install Django and DRF

    python -m venv venv
    source venv/bin/activate  # Windows: venv\Scripts\activate
    pip install django djangorestframework
    

    2. Create a New Django Project

    django-admin startproject myapi
    cd myapi
    python manage.py startapp blog
    

    3. Register Apps in settings.py

    Add 'rest_framework' and your new app ('blog') to INSTALLED_APPS:

    INSTALLED_APPS = [
        # Django defaults …
        'rest_framework',
        'blog',
    ]
    

    4. Configure a Simple Global DRF Setting

    For a quick start, enable the browsable API and set default pagination:

    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 10,
    }
    

    Modeling Your Data

    Let’s build a tiny blog API. First, define a Post model inside blog/models.py:

    from django.db import models
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    class Post(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
        created_at = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return self.title
    

    Run migrations:

    python manage.py makemigrations
    python manage.py migrate
    

    Serializers: Turning Models into JSON

    Serializers are the heart of DRF. They define how model instances are converted to native Python datatypes and then rendered as JSON.

    Creating a PostSerializer

    # blog/serializers.py
    from rest_framework import serializers
    from .models import Post
    
    class PostSerializer(serializers.ModelSerializer):
        author = serializers.ReadOnlyField(source='author.username')
    
        class Meta:
            model = Post
            fields = ['id', 'title', 'content', 'author', 'created_at']
    

    Views: Handling Requests

    DRF offers several view styles. For a CRUD API, ModelViewSet is the most concise.

    Define a PostViewSet

    # blog/views.py
    from rest_framework import viewsets, permissions
    from .models import Post
    from .serializers import PostSerializer
    
    class PostViewSet(viewsets.ModelViewSet):
        queryset = Post.objects.all().order_by('-created_at')
        serializer_class = PostSerializer
        permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    
        def perform_create(self, serializer):
            # Automatically assign the logged‑in user as the author
            serializer.save(author=self.request.user)
    

    Routing: Connecting URLs to Views

    DRF’s DefaultRouter automatically creates the standard list, create, retrieve, update, and destroy routes.

    Register the ViewSet

    # myapi/urls.py
    from django.contrib import admin
    from django.urls import path, include
    from rest_framework.routers import DefaultRouter
    from blog.views import PostViewSet
    
    router = DefaultRouter()
    router.register(r'posts', PostViewSet, basename='post')
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/', include(router.urls)),
        path('api-auth/', include('rest_framework.urls')),  # browsable API login
    ]
    

    Authentication & Permissions

    Secure your API with token authentication or JWT. Below is a quick setup for DRF’s built‑in token auth.

    Install Token Auth

    pip install djangorestframework-simplejwt
    

    Add the authentication classes to settings.py:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ),
        # other settings …
    }
    

    Create Token Endpoints

    # myapi/urls.py (add)
    from rest_framework_simplejwt.views import (
        TokenObtainPairView,
        TokenRefreshView,
    )
    
    urlpatterns += [
        path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    ]
    

    Now clients can obtain a JWT by POSTing {"username": "user", "password": "pass"} to /api/token/ and include the token in the Authorization: Bearer <token> header for protected endpoints.

    Advanced Features

    Custom Pagination

    Sometimes you need cursor‑based pagination for large data sets. Define a custom paginator:

    # blog/pagination.py
    from rest_framework.pagination import CursorPagination
    
    class PostCursorPagination(CursorPagination):
        page_size = 10
        ordering = '-created_at'
    

    Then apply it to the viewset:

    class PostViewSet(viewsets.ModelViewSet):
        # …
        pagination_class = PostCursorPagination
    

    Filtering and Searching

    DRF integrates with django-filter for powerful query‑parameter filtering.

    pip install django-filter
    

    Update settings.py and the viewset:

    # settings.py
    REST_FRAMEWORK['DEFAULT_FILTER_BACKENDS'] = ['django_filters.rest_framework.DjangoFilterBackend',
                                                'rest_framework.filters.SearchFilter']
    
    # blog/views.py
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework import filters
    
    class PostViewSet(viewsets.ModelViewSet):
        # …
        filter_backends = [DjangoFilterBackend, filters.SearchFilter]
        filterset_fields = ['author__username']
        search_fields = ['title', 'content']
    

    Versioning Your API

    Maintain backward compatibility by versioning URLs:

    # myapi/urls.py
    from rest_framework.versioning import URLPathVersioning
    
    router = DefaultRouter()
    router.register(r'posts', PostViewSet, basename='post')
    
    urlpatterns = [
        path('api/v1/', include((router.urls, 'v1'), namespace='v1')),
        # future versions can be added as /api/v2/ …
    ]
    

    Testing Your API

    DRF provides an APIClient that works like Django’s test client but handles JSON automatically.

    Sample Test Case

    # blog/tests.py
    from django.urls import reverse
    from rest_framework.test import APITestCase
    from rest_framework import status
    from django.contrib.auth import get_user_model
    from .models import Post
    
    User = get_user_model()
    
    class PostAPITests(APITestCase):
        def setUp(self):
            self.user = User.objects.create_user(username='tester', password='secret')
            self.client.login(username='tester', password='secret')
            self.post = Post.objects.create(
                title='First Post',
                content='Hello DRF!',
                author=self.user
            )
    
        def test_list_posts(self):
            url = reverse('v1:post-list')
            response = self.client.get(url)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(len(response.data['results']), 1)
    
        def test_create_post(self):
            url = reverse('v1:post-list')
            data = {'title': 'New Post', 'content': 'Testing create'}
            response = self.client.post(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
            self.assertEqual(Post.objects.count(), 2)
    

    Deploying a DRF API to Production

    • Use a WSGI server: Gunicorn or uWSGI behind Nginx.
    • Static & Media Files: Serve via Nginx or a CDN.
    • Environment Variables: Keep secret keys and DB credentials out of source control.
  • Python Flask E-Commerce Cart Management

    Running an online store with Python Flask is a rewarding challenge, especially when you nail the cart experience. A smooth, reliable shopping cart keeps customers engaged, reduces abandonment, and boosts revenue. In this guide we’ll explore every essential piece of Flask e‑commerce cart management – from session handling and database design to secure checkout flows and performance tricks. Whether you’re building a tiny boutique or a large marketplace, the patterns covered here will help you create a cart that feels fast, intuitive, and ready for real‑world traffic.

    Why Flask Is a Great Choice for E‑Commerce Cart Logic

    Flask’s lightweight core gives you full control over how the cart works, without the overhead of a full‑stack framework. You can:

    • Customize data storage: choose server‑side sessions, Redis, or a relational database.
    • Integrate third‑party services: payment gateways, inventory APIs, and recommendation engines.
    • Scale selectively: add blueprints or micro‑services only where you need them.

    These advantages translate into a cart that can evolve with your business, while keeping the codebase clean and maintainable.

    Core Concepts of Cart Management in Flask

    1. Session vs. Persistent Cart Storage

    The first design decision is where to keep the cart data. Two common approaches are:

    1. Session‑based cart: Store items in Flask’s signed cookies or server‑side session store (e.g., Redis). Ideal for guest shoppers and quick prototypes.
    2. Database‑backed cart: Persist cart rows in a relational table linked to a user or a temporary token. Necessary for logged‑in users, multi‑device syncing, and long‑term cart recovery.

    Most production sites use a hybrid: keep a lightweight copy in the session for instant UI updates, and sync it to the database on login or checkout.

    2. Data Model Design

    A clear schema makes cart operations straightforward. Below is a minimal example using SQLAlchemy:

    class CartItem(db.Model):
        __tablename__ = 'cart_items'
        id = db.Column(db.Integer, primary_key=True)
        user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
        session_id = db.Column(db.String(64), nullable=True)  # for guests
        product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
        quantity = db.Column(db.Integer, default=1, nullable=False)
        added_at = db.Column(db.DateTime, default=datetime.utcnow)
    
        __table_args__ = (
            db.UniqueConstraint('user_id', 'session_id', 'product_id', name='uq_cart_product'),
        )
    

    Key points:

    • Either user_id or session_id is populated, never both.
    • A unique constraint prevents duplicate rows for the same product.
    • Storing added_at enables “recently added” features.

    3. Adding, Updating, and Removing Items

    All cart actions should be atomic and idempotent. Here’s a concise Flask view that handles add‑to‑cart requests:

    @app.route('/cart/add', methods=['POST'])
    def add_to_cart():
        product_id = request.form.get('product_id')
        qty = int(request.form.get('quantity', 1))
        # Resolve owner: logged‑in user or session token
        owner = current_user.id if current_user.is_authenticated else session.get('cart_id')
        if not owner:
            # generate a temporary token for guest carts
            owner = uuid4().hex
            session['cart_id'] = owner
    
        item = CartItem.query.filter_by(
            product_id=product_id,
            user_id=current_user.id if current_user.is_authenticated else None,
            session_id=owner if not current_user.is_authenticated else None
        ).first()
    
        if item:
            item.quantity += qty
        else:
            item = CartItem(
                product_id=product_id,
                quantity=qty,
                user_id=current_user.id if current_user.is_authenticated else None,
                session_id=owner if not current_user.is_authenticated else None
            )
            db.session.add(item)
    
        db.session.commit()
        flash('Product added to your cart!', 'success')
        return redirect(url_for('view_cart'))
    

    Notice the use of flash for user feedback and the graceful handling of both guest and authenticated states.

    Implementing a Secure Checkout Flow

    Once the cart is ready, the checkout process must protect user data, validate inventory, and integrate with payment processors. Follow these steps:

    • Validate cart contents: Re‑query product stock to avoid race conditions.
    • Calculate totals server‑side: Never trust client‑side price calculations.
    • Create an order record: Store order items, user info, and a unique order reference.
    • Initiate payment: Use a library like stripe or paypalrestsdk to generate a payment intent.
    • Handle webhooks: Confirm payment status asynchronously and update order state.

    Sample Checkout Endpoint

    @app.route('/checkout', methods=['POST'])
    @login_required
    def checkout():
        cart_items = CartItem.query.filter_by(user_id=current_user.id).all()
        if not cart_items:
            flash('Your cart is empty.', 'warning')
            return redirect(url_for('view_cart'))
    
        # Verify stock and compute total
        total = 0
        for item in cart_items:
            product = Product.query.get(item.product_id)
            if product.stock < item.quantity:
                flash(f'Insufficient stock for {product.name}.', 'danger')
                return redirect(url_for('view_cart'))
            total += product.price * item.quantity
    
        # Create order record
        order = Order(user_id=current_user.id, total_amount=total, status='pending')
        db.session.add(order)
        db.session.flush()  # obtain order.id
    
        # Transfer cart items to order items
        for item in cart_items:
            order_item = OrderItem(
                order_id=order.id,
                product_id=item.product_id,
                quantity=item.quantity,
                price=Product.query.get(item.product_id).price
            )
            db.session.add(order_item)
    
        # Initiate Stripe payment
        intent = stripe.PaymentIntent.create(
            amount=int(total * 100),  # cents
            currency='usd',
            metadata={'order_id': order.id}
        )
        order.payment_intent_id = intent.id
        db.session.commit()
    
        return render_template('checkout.html', client_secret=intent.client_secret)
    

    Performance Tips for High‑Traffic Stores

    Even a well‑structured cart can become a bottleneck under load. Keep your store responsive with these optimizations:

    • Cache product lookups: Use flask-caching or Redis to store price and stock data for a short TTL.
    • Batch database writes: When a user updates many items, commit once instead of per‑item.
    • Asynchronous webhook handling: Process payment confirmations in a background worker (Celery, RQ).
    • Stateless session storage: Store guest cart data in Redis with an expiration, reducing cookie size.
    • Lazy loading for cart UI: Load cart summary via AJAX so the main page renders instantly.

    Testing and Debugging Your Cart Logic

    Automated tests catch edge cases before they affect shoppers. Include:

    1. Unit tests: Verify add, update, and remove functions using pytest and a temporary SQLite database.
    2. Integration tests: Simulate a full checkout flow with Flask‑Testing or requests against a staging server.
    3. Load tests: Use locust or k6 to emulate thousands of concurrent cart actions.

    Example unit test for the add‑to‑cart view:

    def test_add_to_cart(client, db):
        # Create a dummy product
        product = Product(name='Test Shirt', price=29.99, stock=10)
        db.session.add(product)
        db.session.commit()
    
        response = client.post('/cart/add', data={'product_id': product.id, 'quantity': 2})
        assert response.status_code == 302  # redirect after flash
    
        # Verify cart entry in session (guest)
        with client.session_transaction() as sess:
            cart_id = sess['cart_id']
        cart_item = CartItem.query.filter_by(session_id=cart_id, product_id=product.id).first()
        assert cart_item is not None
        assert cart_item.quantity == 2
    

    Best Practices for SEO‑Friendly Cart Pages

    Search engines don’t index cart pages directly, but a well‑optimized cart contributes to overall site health:

    • Fast load times: Aim for under 1 second Time‑to‑First‑Byte (TTFB) on cart pages.
    • Structured data: Add schema.org Offer and Product markup for each line item to help crawlers understand pricing.
    • Canonical URLs: Prevent duplicate content by using a single canonical URL for the cart (e.g., /cart).
    • Mobile‑first design: Use responsive CSS so
  • Python Flask Real-Time Chat With Socketio

    Imagine a chat application that feels instant, responsive, and runs on a lightweight Python framework you already love. With Flask and SocketIO, building a real‑time messaging system is not only possible—it’s surprisingly straightforward. In this guide we’ll walk through every step, from setting up the development environment to deploying a production‑ready chat app, while sprinkling in SEO‑friendly keywords like “Python Flask real‑time chat”, “Flask‑SocketIO”, and “WebSocket integration”. Whether you’re a beginner eager to experiment or a seasoned developer looking for a quick prototype, this tutorial has you covered.

    Why Choose Flask and SocketIO for Real‑Time Chat?

    Lightweight yet powerful

    Flask’s minimal core lets you add only the extensions you need, keeping the codebase clean and maintainable. Pair it with Flask‑SocketIO, and you get a seamless bridge to WebSocket technology without leaving the Python ecosystem.

    Key advantages

    • Simple routing: Flask’s intuitive route decorators make URL handling a breeze.
    • Bi‑directional communication: SocketIO enables real‑time, event‑driven messaging between client and server.
    • Scalability: With support for message queues like Redis or RabbitMQ, you can scale horizontally across multiple workers.
    • Cross‑browser compatibility: SocketIO falls back to long‑polling when WebSockets aren’t available, ensuring a consistent user experience.

    Setting Up the Development Environment

    Prerequisites

    Before writing code, make sure you have the following installed:

    1. Python 3.8+ (download from python.org)
    2. pip – the Python package manager
    3. Virtualenv (optional but recommended for isolated environments)

    Installing Flask and Flask‑SocketIO

    # Create and activate a virtual environment
    python -m venv venv
    source venv/bin/activate   # On Windows use `venv\Scripts\activate`
    
    # Install core packages
    pip install Flask Flask-SocketIO eventlet
    

    We use eventlet as the asynchronous server because it provides excellent performance for WebSocket traffic.

    Building the Basic Flask Application

    Start with a minimal Flask app that serves an HTML page. Save the file as app.py:

    from flask import Flask, render_template
    from flask_socketio import SocketIO
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'replace-with-a-secure-key'
    socketio = SocketIO(app, async_mode='eventlet')
    
    @app.route('/')
    def index():
        return render_template('index.html')
    
    if __name__ == '__main__':
        socketio.run(app, debug=True)
    

    Next, create a templates folder and add index.html. This file will host the chat UI and load the SocketIO client library.

    Integrating SocketIO for Real‑Time Messaging

    In app.py, define the server‑side events that handle incoming messages and broadcast them to all connected clients.

    @socketio.on('join')
    def handle_join(data):
        username = data['username']
        room = data['room']
        join_room(room)
        send(f"{username} has entered the room.", room=room)
    
    @socketio.on('message')
    def handle_message(data):
        room = data['room']
        msg = data['msg']
        send(msg, room=room)
    
    @socketio.on('disconnect')
    def handle_disconnect():
        print('Client disconnected')
    

    Notice the use of join_room and send—both are part of the Flask‑SocketIO API and simplify room‑based chat logic.

    Creating the Front‑End UI

    The front‑end consists of a simple form for username and room selection, plus a message input. Below is a concise index.html example that leverages Bootstrap for quick styling.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Flask Real‑Time Chat</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
        <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
    </head>
    <body class="bg-light">
        <div class="container py-5">
            <h2 class="mb-4">Python Flask Real‑Time Chat</h2>
            <div id="login" class="mb-3">
                <input id="username" class="form-control mb-2" placeholder="Enter your name">
                <input id="room" class="form-control mb-2" placeholder="Room name (e.g., general)">
                <button id="joinBtn" class="btn btn-primary">Join Chat</button>
            </div>
            <div id="chat" class="d-none">
                <ul id="messages" class="list-group mb-3"></ul>
                <input id="msgInput" class="form-control" placeholder="Type a message..." autocomplete="off">
            </div>
        </div>
        <script>
            const socket = io();
    
            document.getElementById('joinBtn').onclick = () => {
                const username = document.getElementById('username').value.trim();
                const room = document.getElementById('room').value.trim();
                if (!username || !room) return alert('Both fields are required');
                socket.emit('join', {username, room});
                document.getElementById('login').classList.add('d-none');
                document.getElementById('chat').classList.remove('d-none');
            };
    
            socket.on('message', data => {
                const li = document.createElement('li');
                li.textContent = data;
                li.className = 'list-group-item';
                document.getElementById('messages').appendChild(li);
            });
    
            document.getElementById('msgInput').addEventListener('keypress', e => {
                if (e.key === 'Enter') {
                    const msg = e.target.value;
                    const room = document.getElementById('room').value;
                    socket.emit('message', {room, msg});
                    e.target.value = '';
                }
            });
        </script>
    </body>
    </html>
    

    Key points in the UI:

    • Dynamic room joining: Users specify a room name, enabling multiple independent chat channels.
    • Live message list: Incoming messages are appended to a Bootstrap list group in real time.
    • Enter‑key shortcut: Pressing Enter sends the message instantly, mimicking native chat apps.

    Handling Events and Broadcasting Messages

    Flask‑SocketIO provides a rich set of events you can customize. Below is a quick reference for the most common ones used in a chat context:

    
    # Server‑side events
    @socketio.on('connect')          # Triggered when a client connects
    @socketio.on('disconnect')       # Triggered when a client disconnects
    @socketio.on('join')             # Custom event to add a user to a room
    @socketio.on('message')          # Handles incoming chat messages
    @socketio.on('leave')            # Optional: remove a user from a room
    

    When you call send(message, room=room), SocketIO automatically broadcasts the payload to every socket that has joined the specified room. This eliminates the need for manual iteration over client connections.

    Deploying the Chat App to Production

    Running a Flask‑SocketIO app in development mode (with debug=True) is fine for testing, but production requires a more robust server stack.

    Recommended deployment stack

    1. Gunicorn with the eventlet worker class for asynchronous handling.
    2. NGINX as a reverse proxy to manage SSL termination and static assets.
    3. Redis (or RabbitMQ) as a message broker if you plan to scale across multiple worker processes.

    Sample Gunicorn command

    gunicorn -k eventlet -w 4 app:app
    

    For multi‑process scaling with Redis, initialize SocketIO with a message queue:

    socketio = SocketIO(app, message_queue

  • Python Flask Microservices Architecture

    In today’s fast‑moving software landscape, the demand for scalable, maintainable, and independently deployable services has turned microservices into a de‑facto architectural pattern. When you pair the lightweight nature of Flask with Python’s rich ecosystem, you get a powerful foundation for building Python Flask microservices that can evolve alongside your business. This guide walks you through the core concepts, best practices, and step‑by‑step implementation details you need to design a robust Flask‑based microservices architecture.

    Why Choose Flask for Microservices?

    Flask is a micro‑framework by design, which means it gives you just enough tools to get an HTTP service up and running without imposing heavy conventions. This minimalism aligns perfectly with the microservices philosophy of “do one thing well.” Here are the key reasons developers gravitate toward Flask for microservices:

    • Lightweight core: Only the essentials are bundled, keeping the runtime footprint low.
    • Extensible: A vibrant plugin ecosystem (e.g., Flask‑RESTful, Flask‑JWT, Flask‑SQLAlchemy) lets you add functionality only when you need it.
    • Python friendliness: Leverage Python’s readability, extensive libraries, and rapid development cycle.
    • Easy testing: Flask’s built‑in test client simplifies unit and integration testing of each service.

    Core Principles of a Flask Microservices Architecture

    1. Single Responsibility per Service

    Each Flask application should own a distinct business capability—user authentication, order processing, inventory management, etc. This isolation reduces coupling and enables independent scaling.

    2. Statelessness

    Microservices thrive when they do not retain client‑specific state between requests. Store session data in external stores like Redis or use JWT tokens to keep services stateless and horizontally scalable.

    3. API‑First Design

    Define clear, versioned RESTful (or gRPC) contracts before writing code. Tools such as OpenAPI/Swagger help generate documentation and client SDKs automatically.

    4. Decentralized Data Management

    Every service owns its own database schema. Avoid shared databases; instead, use data replication or event‑driven synchronization when cross‑service data is required.

    Building a Flask Microservice: Step‑by‑Step

    Step 1: Project Layout

    A consistent directory structure makes onboarding and maintenance easier. Below is a recommended layout:

    myservice/
    ├── app/
    │   ├── __init__.py
    │   ├── config.py
    │   ├── routes/
    │   │   ├── __init__.py
    │   │   └── user.py
    │   └── models/
    │       └── user.py
    ├── tests/
    │   └── test_user.py
    ├── Dockerfile
    ├── requirements.txt
    └── run.py
    

    Step 2: Initialize the Flask Application

    In app/__init__.py, create the Flask instance and register blueprints for modular routing:

    from flask import Flask
    from .routes import user_bp
    
    def create_app(config_name='development'):
        app = Flask(__name__)
        app.config.from_object(f'app.config.{config_name.capitalize()}Config')
        app.register_blueprint(user_bp, url_prefix='/api/v1/users')
        return app
    

    Step 3: Define a RESTful Endpoint

    Use Flask‑RESTful or plain Flask routes. Here’s an example using a blueprint:

    from flask import Blueprint, request, jsonify
    from ..models.user import User, db
    
    user_bp = Blueprint('user', __name__)
    
    @user_bp.route('/', methods=['POST'])
    def create_user():
        data = request.get_json()
        new_user = User(**data)
        db.session.add(new_user)
        db.session.commit()
        return jsonify(new_user.to_dict()), 201
    

    Step 4: Add Validation and Authentication

    Integrate flask-jwt-extended for token‑based security and marshmallow for schema validation:

    from flask_jwt_extended import jwt_required
    from marshmallow import Schema, fields, ValidationError
    
    class UserSchema(Schema):
        username = fields.Str(required=True)
        email = fields.Email(required=True)
    
    @user_bp.route('/', methods=['POST'])
    @jwt_required()
    def create_user():
        try:
            validated = UserSchema().load(request.get_json())
        except ValidationError as err:
            return jsonify(err.messages), 400
        # proceed with creation...
    

    Step 5: Containerize with Docker

    Docker isolates dependencies and ensures consistent deployments across environments. A minimal Dockerfile for Flask looks like this:

    FROM python:3.11-slim
    
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . .
    ENV FLASK_APP=run.py
    ENV FLASK_ENV=production
    
    CMD ["gunicorn", "--bind", "0.0.0.0:8080", "run:app"]
    

    Step 6: Orchestrate with Kubernetes

    Deploy each Flask service as a separate Kubernetes Deployment and expose it via a Service object. Use an Ingress controller (e.g., NGINX Ingress) to route external traffic to the appropriate microservice based on URL paths or hostnames.

    Cross‑Service Communication Patterns

    Choosing the right communication style is crucial for performance and resilience.

    • Synchronous HTTP/REST: Simple to implement; ideal for request‑response interactions like fetching user details.
    • Asynchronous Messaging: Use RabbitMQ, Kafka, or AWS SQS for event‑driven workflows (e.g., order placed → inventory update).
    • gRPC: Binary protocol offering higher throughput and contract enforcement; useful for internal high‑traffic services.

    Observability and Monitoring

    Without visibility, microservices become a black box. Implement the following observability layers:

    1. Logging: Structured JSON logs with correlation IDs (e.g., request‑id) sent to a centralized system like ELK or Loki.
    2. Metrics: Export Prometheus metrics via flask‑prometheus or prometheus‑flask‑exporter for request latency, error rates, and resource usage.
    3. Tracing: Use OpenTelemetry to trace requests across service boundaries, visualizing them in Jaeger or Zipkin.

    Security Best Practices

    Security cannot be an afterthought. Follow these guidelines to harden your Flask microservices:

    • Enforce HTTPS with TLS termination at the Ingress level.
    • Validate all incoming data—never trust client input.
    • Implement rate limiting (e.g., Flask-Limiter) to mitigate DoS attacks.
    • Rotate secrets regularly and store them in a vault such as HashiCorp Vault or AWS Secrets Manager.
    • Run containers with non‑root users and minimal capabilities.

    Testing Strategies for Flask Microservices

    Robust testing ensures each microservice behaves as expected in isolation and when integrated.

    Unit Tests

    Mock external dependencies (databases, external APIs) using unittest.mock or pytest‑mock. Example:

    def test_create_user_success(client, mocker):
        mocker.patch('app.models.user.db.session.commit')
        response = client.post('/api/v1/users/', json={'username': 'alice', 'email': 'a@example.com'})
        assert response.status_code == 201
    

    Integration Tests

    Spin up a Docker Compose environment that includes the service, its database, and any required message brokers. Run end‑to‑end scenarios against the real stack.

    Contract Tests

    Use Pact or Schemathesis to verify that the service adheres to its OpenAPI contract, preventing breaking changes from slipping into production.

    Scaling Flask Microservices

    When traffic spikes, you can scale horizontally by increasing the replica count in Kubernetes. Combine this with:

    • Auto‑scaling: Kubernetes Horizontal Pod Autoscaler (HPA) based on CPU or custom Prometheus metrics.
    • Load balancing: Service mesh (e.g., Istio) for intelligent traffic routing, retries, and circuit breaking.
    • Cache layer: Redis or Memcached for read‑heavy endpoints to reduce database load.

    Versioning and Deployment Strategies

    To avoid downtime during updates, adopt one of these proven patterns:

    1. Blue/Green Deployments: Run two identical environments; switch traffic after successful validation.
    2. Canary Releases: Gradually route a small percentage of traffic to the new version, monitoring for errors before full rollout.
    3. Feature Flags: Deploy code with toggles that enable/disable new functionality at runtime.

    Common Pitfalls and How to Avoid Them

    • Over‑centralizing data: Resist the urge to create a shared database; it defeats the purpose of microservices.
    • Ignoring network latency
  • Python Flask Role-Based Access Control

    When you build a web application with Python Flask, securing your endpoints is as important as delivering the right features. One of the most scalable ways to manage permissions is role‑based access control (RBAC). In this guide you’ll learn how to implement RBAC in Flask using popular extensions, custom decorators, and best‑practice patterns that keep your code clean, testable, and ready for production.

    What Is Role‑Based Access Control?

    RBAC is a security model that assigns permissions to roles rather than to individual users. A user can have one or many roles, and each role aggregates the actions that its members are allowed to perform. This approach simplifies permission management, especially as your user base grows.

    • Roles: Logical groups such as admin, editor, viewer.
    • Permissions: Specific actions like create_post, delete_user, view_reports.
    • Assignments: Users ↔ roles, roles ↔ permissions.

    Why Choose Flask for RBAC?

    Flask’s lightweight core gives you the freedom to pick the exact tools you need. For RBAC you typically combine:

    • Flask‑Login – handles user session management.
    • Flask‑Principal or Flask‑Security‑Too – provides role and permission abstractions.
    • Custom decorators – enforce role checks at the view level.

    This modular stack keeps your application fast while still offering a robust security foundation.

    Setting Up a Minimal Flask Project

    1. Create the project structure

    my_flask_app/
    ├── app.py
    ├── models.py
    ├── extensions.py
    └── requirements.txt
    

    2. Install required packages

    pip install Flask Flask-Login Flask-Principal Flask-SQLAlchemy
    

    3. Initialize Flask and extensions

    # extensions.py
    from flask_sqlalchemy import SQLAlchemy
    from flask_login import LoginManager
    from flask_principal import Principal
    
    db = SQLAlchemy()
    login_manager = LoginManager()
    principal = Principal()
    

    Defining Users, Roles, and Permissions

    Below is a simple SQLAlchemy model that captures the many‑to‑many relationship between users and roles. Permissions are stored as strings attached to each role.

    # models.py
    from extensions import db
    from flask_login import UserMixin
    
    user_roles = db.Table(
        'user_roles',
        db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
        db.Column('role_id', db.Integer, db.ForeignKey('role.id'))
    )
    
    class Role(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64), unique=True, nullable=False)
        # Permissions are stored as a comma‑separated list for simplicity
        permissions = db.Column(db.String(256), nullable=False)
    
        def has_permission(self, perm):
            return perm in self.permissions.split(',')
    
    class User(UserMixin, db.Model):
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(80), unique=True, nullable=False)
        password_hash = db.Column(db.String(128), nullable=False)
        roles = db.relationship('Role', secondary=user_roles, backref='users')
    
        def has_role(self, role_name):
            return any(role.name == role_name for role in self.roles)
    
        def has_permission(self, perm):
            return any(role.has_permission(perm) for role in self.roles)
    

    Integrating Flask‑Login and Flask‑Principal

    Flask‑Login manages the authentication session, while Flask‑Principal lets us express role and permission requirements as identities and needs.

    # app.py (excerpt)
    from flask import Flask, redirect, url_for, request, abort
    from extensions import db, login_manager, principal
    from models import User, Role
    from flask_login import login_user, logout_user, current_user, login_required
    from flask_principal import Identity, AnonymousIdentity, \
        identity_changed, identity_loaded, RoleNeed, Permission
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'change‑me‑please'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
    
    db.init_app(app)
    login_manager.init_app(app)
    principal.init_app(app)
    
    # ------------------------------------------------------------------
    # Flask‑Login loader
    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))
    
    # ------------------------------------------------------------------
    # Flask‑Principal identity loading
    @identity_loaded.connect_via(app)
    def on_identity_loaded(sender, identity):
        # Set the identity user object
        identity.user = current_user
    
        # Add the UserNeed (provides a unique identifier)
        if not isinstance(current_user, AnonymousIdentity):
            identity.provides.add(RoleNeed('authenticated'))
    
            # Add each role as a Need
            for role in current_user.roles:
                identity.provides.add(RoleNeed(role.name))
    

    Creating Role‑Based Permissions with Flask‑Principal

    Define reusable Permission objects that map to your roles. You can also combine multiple roles in a single permission.

    # permissions.py
    from flask_principal import Permission, RoleNeed
    
    admin_permission = Permission(RoleNeed('admin'))
    editor_permission = Permission(RoleNeed('editor'))
    viewer_permission = Permission(RoleNeed('viewer'))
    
    # Composite permission (admin OR editor)
    admin_or_editor = Permission(RoleNeed('admin'), RoleNeed('editor'))
    

    Protecting Routes with Decorators

    While Flask‑Principal lets you call permission.require() inside a view, a cleaner pattern is to create a decorator that handles the check and returns 403 Forbidden automatically.

    # utils.py
    from functools import wraps
    from flask import abort
    from flask_principal import Permission
    
    def requires_permission(*needs):
        """Decorator that checks one or more RoleNeed objects."""
        permission = Permission(*needs)
    
        def decorator(f):
            @wraps(f)
            def decorated_function(*args, **kwargs):
                if not permission.can():
                    abort(403)
                return f(*args, **kwargs)
            return decorated_function
        return decorator
    

    Applying the decorator

    # app.py (continued)
    from utils import requires_permission
    from flask_principal import RoleNeed
    
    @app.route('/admin/dashboard')
    @login_required
    @requires_permission(RoleNeed('admin'))
    def admin_dashboard():
        return 'Welcome to the admin dashboard.'
    
    @app.route('/posts/create')
    @login_required
    @requires_permission(RoleNeed('editor'), RoleNeed('admin'))
    def create_post():
        return 'Post creation form.'
    
    @app.route('/reports')
    @login_required
    @requires_permission(RoleNeed('viewer'), RoleNeed('admin'))
    def view_reports():
        return 'Here are the reports.'
    

    Testing Your RBAC Implementation

    Automated tests ensure that role changes don’t accidentally open security holes.

    • Use FlaskClient to simulate login for different users.
    • Assert 200 OK for allowed routes and 403 Forbidden for denied routes.
    # test_rbac.py
    import pytest
    from app import app, db
    from models import User, Role
    
    @pytest.fixture
    def client():
        app.config['TESTING'] = True
        with app.test_client() as client:
            with app.app_context():
                db.create_all()
                # Create roles
                admin = Role(name='admin', permissions='create,delete,view')
                editor = Role(name='editor', permissions='create,view')
                viewer = Role(name='viewer', permissions='view')
                db.session.add_all([admin, editor, viewer])
                db.session.commit()
            yield client
            db.drop_all()
    
    def login(client, username, password):
        return client.post('/login', data=dict(username=username, password=password), follow_redirects=True)
    
    def test_admin_access(client):
        # Assume a user with admin role exists
        login(client, 'alice', 'secret')
        resp = client.get('/admin/dashboard')
        assert resp.status_code == 200
    
    def test_editor_cannot_access_admin(client):
        login(client, 'bob', 'secret')
        resp = client.get('/admin/dashboard')
        assert resp.status_code == 403
    

    Best Practices for Flask RBAC

    • Store permissions in a dedicated table. Keeping permissions separate from roles makes it easier to add fine‑grained actions later.
    • Never hard‑code role names. Use constants or an enum to avoid typos.
    • Cache role lookups. For high‑traffic apps, cache the user’s role list in the session to reduce DB hits.
    • Log denied access attempts. Auditing helps you spot misconfigurations or malicious probing.
    • Combine RBAC with attribute‑based checks. Sometimes you need both role and object ownership checks; wrap them in a single decorator for clarity.

    Common Pitfalls and How to Avoid Them

    1. Mixing authentication and authorization logic

    Keep login_required (authentication) separate from role checks. This makes the code easier to read and prevents accidental exposure of protected data.

    2. Forgetting to reload the identity after role changes

    If you modify a user’s roles at runtime, call identity_changed.send(app, identity

  • Python Flask Portfolio Website Project

    Building a Python Flask portfolio website project is an excellent way to showcase your development skills, demonstrate real‑world problem solving, and create a personal brand that stands out to recruiters and clients. In this guide, we’ll walk through every step—from setting up the Flask environment and designing a clean UI, to deploying the site on a cloud platform—while highlighting best practices for SEO, security, and maintainability. Whether you’re a beginner looking for a first project or an experienced developer wanting a polished showcase, this tutorial provides a complete, production‑ready roadmap.

    Why Choose Flask for Your Portfolio?

    Flask is a lightweight, micro‑framework that gives you full control over the architecture of your site. Here are a few reasons it’s ideal for a portfolio project:

    • Flexibility: No forced directory structure; you can organize templates, static files, and routes exactly how you want.
    • Extensibility: Easily add extensions for databases, authentication, or SEO without bloat.
    • Python ecosystem: Leverage powerful libraries such as Jinja2 for templating, SQLAlchemy for ORM, and Flask‑Login for user management.
    • Scalability: Start simple, then grow into a full‑stack application with APIs, background tasks, or micro‑services.

    Project Structure Overview

    A clean project layout makes development smoother and helps search engines crawl your site efficiently. Below is a recommended directory tree for the flask_portfolio project:

    flask_portfolio/
    │
    ├─ app/
    │   ├─ __init__.py          # Flask app factory
    │   ├─ routes.py            # View functions & URL mappings
    │   ├─ models.py            # Database models (if any)
    │   ├─ forms.py             # WTForms for contact forms
    │   ├─ static/
    │   │   ├─ css/
    │   │   │   └─ style.css
    │   │   ├─ js/
    │   │   │   └─ main.js
    │   │   └─ images/
    │   └─ templates/
    │       ├─ base.html
    │       ├─ index.html
    │       ├─ projects.html
    │       └─ contact.html
    │
    ├─ migrations/              # Flask‑Migrate files (optional)
    ├─ tests/                   # Unit and integration tests
    ├─ .env                     # Environment variables
    ├─ config.py                # Configuration classes
    ├─ requirements.txt         # Python dependencies
    └─ run.py                   # Entry point

    Setting Up the Development Environment

    1. Create a Virtual Environment

    Isolating dependencies ensures your portfolio won’t conflict with other Python projects.

    python -m venv venv
    source venv/bin/activate   # On Windows: venv\Scripts\activate

    2. Install Flask and Core Extensions

    Run the following command to install the essential packages:

    pip install Flask Flask-WTF Flask-Login Flask-Migrate python-dotenv

    3. Initialize the Flask App Factory

    Using an app factory pattern keeps the code modular and testable.

    # app/__init__.py
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_migrate import Migrate
    from flask_login import LoginManager
    from dotenv import load_dotenv
    import os
    
    load_dotenv()  # Load .env variables
    
    db = SQLAlchemy()
    migrate = Migrate()
    login_manager = LoginManager()
    
    def create_app():
        app = Flask(__name__)
        app.config.from_object('config.Config')
    
        db.init_app(app)
        migrate.init_app(app, db)
        login_manager.init_app(app)
    
        with app.app_context():
            from . import routes  # Register routes
            return app

    Designing SEO‑Friendly Templates

    Search engine optimization starts with clean HTML, meaningful meta tags, and fast loading assets. Below are key elements to include in each template.

    Base Template (base.html)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ title }} | My Flask Portfolio</title>
        <meta name="description" content="{{ meta_description }}">
        <link rel="canonical" href="{{ request.url }}">
        <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
        <!-- Open Graph for social sharing -->
        <meta property="og:title" content="{{ title }}">
        <meta property="og:description" content="{{ meta_description }}">
        <meta property="og:type" content="website">
        <meta property="og:url" content="{{ request.url }}">
        <meta property="og:image" content="{{ url_for('static', filename='images/og-image.jpg') }}">
    </head>
    <body>
        {% include 'navbar.html' %}
        <main>
            {{ content|safe }}
        </main>
        {% include 'footer.html' %}
        <script src="{{ url_for('static', filename='js/main.js') }}" defer></script>
    </body>
    </html>

    Home Page (index.html)

    Showcase a hero section, a brief bio, and featured projects. Use h2 and h3 tags strategically for keyword hierarchy.

    {% extends 'base.html' %}
    {% block title %}Home{% endblock %}
    {% block meta_description %}Professional Python developer portfolio featuring Flask projects, open‑source contributions, and contact information.{% endblock %}
    
    {% block content %}
    
    <h2>Hi, I’m Alex – Python & Flask Enthusiast</h2> <p>Building clean, scalable web applications that solve real problems.</p> <a href="{{ url_for('projects') }}" class="btn">View My Projects</a>
    <h3>About Me</h3> <p>With 4+ years of experience in backend development, I specialize in Flask, REST APIs, and cloud deployment. My portfolio reflects a blend of personal projects, client work, and contributions to open‑source libraries.</p>
    {% endblock %}

    Implementing Core Features

    1. Project Showcase Page

    Use a simple data model to store project details, then loop through them in the template.

    # app/models.py
    from . import db
    
    class Project(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(120), nullable=False)
        description = db.Column(db.Text, nullable=False)
        url = db.Column(db.String(200), nullable=True)
        image = db.Column(db.String(200), nullable=True)
        tech_stack = db.Column(db.String(200), nullable=False)
    

    In routes.py:

    # app/routes.py
    from flask import render_template, current_app as app
    from .models import Project
    
    @app.route('/projects')
    def projects():
        all_projects = Project.query.all()
        return render_template('projects.html', title='Projects', meta_description='My Flask portfolio projects', projects=all_projects)

    2. Contact Form with Flask‑WTF

    A functional contact form improves user engagement and provides a direct lead channel.

    # app/forms.py
    from flask_wtf import FlaskForm
    from wtforms import StringField, TextAreaField, SubmitField
    from wtforms.validators import DataRequired, Email, Length
    
    class ContactForm(FlaskForm):
        name = StringField('Name', validators=[DataRequired(), Length(max=50)])
        email = StringField('Email', validators=[DataRequired(), Email(), Length(max=120)])
        message = TextAreaField('Message', validators=[DataRequired(), Length(max=2000)])
        submit = SubmitField('Send')
    

    3. SEO Enhancements

    • Semantic HTML: Use <header>, <nav>, <section>, and <article> where appropriate.
    • Keyword‑rich headings: Include “Python Flask portfolio website project” in at least one h2 and a few h3 tags.
    • Alt attributes: Every image gets an alt description that mentions relevant keywords.
    • Schema.org markup: Add JSON‑LD for a Person schema to boost rich results.

    Testing and Quality Assurance

    Before going live, run both unit tests and a quick performance audit.

    Unit Tests with Pytest

    # tests/test_routes.py
    import pytest
    from flask import url_for
    
    def test_home_page(client):
        response = client.get(url_for('index'))
        assert response.status_code == 200
        assert b'Python Flask portfolio' in response.data
    
    def test_projects_page(client):
        response = client.get(url_for('projects'))
        assert response.status_code == 200
        assert b'Projects' in response.data
    

  • Python Flask File Upload Security Guide

    Uploading files is one of the most common features in modern web applications, and Flask makes it incredibly easy to add this functionality with just a few lines of code. However, every file upload is a potential attack vector: malicious scripts, oversized payloads, and path traversal exploits can all compromise your server if you’re not careful. This Python Flask file upload security guide walks you through the essential steps—validation, storage, scanning, and best‑practice configuration—to keep your Flask app safe while still providing a smooth user experience.

    Why File Upload Security Matters in Flask

    Flask’s minimalist design gives you full control over request handling, but that freedom also means you’re responsible for implementing robust security checks. An insecure upload endpoint can lead to:

    • Remote code execution if an attacker uploads a script that the server executes.
    • Denial of service through oversized files that exhaust disk space or memory.
    • Data leakage when sensitive directories are exposed via path traversal.
    • Cross‑site scripting (XSS) if malicious content is later rendered in the browser.

    By following the guidelines below, you’ll mitigate these risks and align your Flask project with industry‑standard security practices.

    Core Principles of Secure File Uploads

    1. Whitelist File Types

    Never rely on the client‑side accept attribute alone. Validate the MIME type and file extension on the server, and preferably inspect the file’s magic bytes.

    ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf'}
    
    def allowed_file(filename):
        return '.' in filename and \
               filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
    

    2. Enforce Size Limits

    Set a maximum request size to prevent large uploads from overwhelming your server.

    # config.py
    MAX_CONTENT_LENGTH = 5 * 1024 * 1024  # 5 MB
    

    Flask will automatically abort requests that exceed this limit, returning a 413 Request Entity Too Large response.

    3. Use Secure Filenames

    Never trust the original filename. Use werkzeug.utils.secure_filename and optionally prepend a UUID or timestamp to avoid collisions.

    from werkzeug.utils import secure_filename
    import uuid
    import os
    
    def generate_secure_filename(filename):
        name = secure_filename(filename)
        unique_name = f"{uuid.uuid4().hex}_{name}"
        return unique_name
    

    4. Store Files Outside the Web Root

    Saving uploads in a directory that isn’t directly served by Flask (or your web server) prevents accidental exposure. Serve files through a controlled route that checks permissions before sending the file.

    Step‑by‑Step Implementation

    Step 1: Configure Flask Settings

    # app.py
    from flask import Flask
    app = Flask(__name__)
    
    app.config.from_pyfile('config.py')
    app.config['UPLOAD_FOLDER'] = os.path.join(app.instance_path, 'uploads')
    os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
    

    Step 2: Create the Upload Endpoint

    from flask import request, abort, jsonify, send_from_directory
    from werkzeug.utils import secure_filename
    import uuid, os
    
    @app.route('/upload', methods=['POST'])
    def upload_file():
        # 1️⃣ Verify request contains a file
        if 'file' not in request.files:
            abort(400, description='No file part in the request')
        file = request.files['file']
    
        # 2️⃣ Check for empty filename
        if file.filename == '':
            abort(400, description='No selected file')
    
        # 3️⃣ Validate extension
        if not allowed_file(file.filename):
            abort(400, description='File type not allowed')
    
        # 4️⃣ Secure and unique filename
        filename = generate_secure_filename(file.filename)
    
        # 5️⃣ Save file safely
        destination = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(destination)
    
        # 6️⃣ Optional: Run virus scan here (see next section)
    
        return jsonify({'message': 'File uploaded successfully', 'filename': filename})
    

    Step 3: Serve Files Securely

    Instead of exposing the upload directory, create a protected route that streams files only after verifying the user’s rights.

    @app.route('/files/<filename>')
    def download_file(filename):
        # Example: check if user is authenticated & authorized
        if not current_user.is_authenticated:
            abort(401)
    
        # Ensure the filename is safe
        safe_name = secure_filename(filename)
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_name)
    
        if not os.path.isfile(file_path):
            abort(404)
    
        return send_from_directory(app.config['UPLOAD_FOLDER'], safe_name)
    

    Advanced Security Enhancements

    Content‑Based Validation

    Relying only on extensions can be bypassed. Use libraries such as python-magic to read the file’s magic number.

    import magic
    
    def is_allowed_mime(file_stream):
        mime = magic.from_buffer(file_stream.read(2048), mime=True)
        file_stream.seek(0)  # Reset pointer after reading
        return mime in {'image/png', 'image/jpeg', 'application/pdf'}
    
    # In the upload route:
    if not is_allowed_mime(file.stream):
        abort(400, description='Invalid file content')
    

    Integrate Antivirus Scanning

    Running a lightweight scanner like ClamAV adds a strong layer of protection.

    import subprocess
    
    def scan_with_clamav(filepath):
        result = subprocess.run(['clamscan', '--no-summary', filepath],
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return result.returncode == 0  # 0 = clean, 1 = infected
    
    # After saving the file:
    if not scan_with_clamav(destination):
        os.remove(destination)
        abort(400, description='Uploaded file failed virus scan')
    

    Rate Limiting & Throttling

    Prevent abuse by limiting how many uploads a single IP can perform in a given timeframe. Flask‑Limiter integrates easily:

    from flask_limiter import Limiter
    from flask_limiter.util import get_remote_address
    
    limiter = Limiter(app, key_func=get_remote_address)
    
    @app.route('/upload', methods=['POST'])
    @limiter.limit('5 per minute')
    def upload_file():
        # existing logic...
        pass
    

    Content‑Security‑Policy (CSP) Headers

    When you later display uploaded images or PDFs, a strict CSP helps mitigate XSS risks.

    @app.after_request
    def set_security_headers(response):
        response.headers['Content-Security-Policy'] = "default-src 'self'; img-src 'self' data:; object-src 'none';"
        response.headers['X-Content-Type-Options'] = 'nosniff'
        response.headers['X-Frame-Options'] = 'DENY'
        return response
    

    Testing Your Upload Security

    • Unit tests: Mock file uploads with both allowed and disallowed types, oversized payloads, and malicious filenames.
    • Fuzz testing: Use tools like ffuf or Burp Suite to send random binary data and verify your endpoint rejects it.
    • Pen‑test checklist:
      1. Attempt to upload a .php script disguised as an image.
      2. Send a file with a path traversal payload (e.g., ../../etc/passwd).
      3. Upload a file larger than MAX_CONTENT_LENGTH.
      4. Check that the uploaded file cannot be accessed directly via URL.

    Common Pitfalls & How to Avoid Them

    • Skipping MIME verification: Attackers can rename executables to .jpg. Always inspect the file’s internal signature.
    • Saving to a public folder: Even with a safe filename, a public directory can expose files to the internet.
    • Hard‑coding paths: Use os.path.join and Flask’s instance_path to keep your code portable across environments.
    • Ignoring error handling: Return clear HTTP status codes (400, 413, 415) so clients can react appropriately.

    SEO Tips for This Guide

    To make this article rank well for the keyword “Python Flask file upload security guide,” we’ve incorporated the phrase naturally in headings, bold text, and throughout the body. Adding related terms such as “secure file uploads in Flask,” “Flask upload validation,” and “prevent file upload vulnerabilities” reinforces relevance. Use descriptive alt text for any future images, and consider linking to authoritative sources like the Flask documentation, OWASP file upload cheat sheet, and ClamAV project page.

    Conclusion

    File uploads are a powerful feature, but they also open the door to a wide range of attacks. By applying a layered defense—whitelisting extensions, validating MIME types, enforcing size limits, sanitizing filenames, storing files outside the web root, scanning for malware, and adding rate limiting—you can protect your Flask application without sacrificing usability. Implement the code snippets, adapt the configuration to your environment, and regularly test your endpoint to stay ahead of emerging threats. With these practices in place, your Python Flask app will handle user uploads safely and confidently.

  • Python Flask Blog Application Tutorial

    Looking to turn your Python skills into a real‑world web project? In this step‑by‑step tutorial you’ll learn how to build a fully functional blog application with Flask, the lightweight yet powerful web framework that powers everything from micro‑services to large‑scale sites. By the end of the guide you’ll have a clean, modular Flask blog that supports posting, editing, deleting, and commenting—all while following best practices for project structure, security, and SEO.

    Why Choose Flask for a Blog?

    Flask is a micro‑framework that gives you just enough tools to get started, while letting you add extensions only when you need them. This makes it an ideal choice for a blog because you can:

    • Keep the codebase lightweight and easy to understand.
    • Customize the database layer (SQLAlchemy, Peewee, or even plain SQLite).
    • Integrate SEO‑friendly features like clean URLs, meta tags, and sitemaps.
    • Scale later with extensions such as Flask‑Login, Flask‑Migrate, and Flask‑Cache.

    Project Overview

    Our Flask blog will include the following core features:

    1. Home page displaying a list of blog posts.
    2. Individual post pages with full content and comments.
    3. Admin interface for creating, editing, and deleting posts.
    4. User authentication (optional, using Flask‑Login).
    5. SEO enhancements: meta titles, descriptions, and a sitemap.

    Prerequisites

    Before you dive in, make sure you have the following installed on your development machine:

    • Python 3.9+ (python --version)
    • Virtualenv (python -m pip install virtualenv)
    • Git (optional, for version control)

    Step 1: Set Up the Project Structure

    A clean folder layout helps you stay organized and makes future maintenance a breeze. Create the directories as shown below:

    flask_blog/
    ├── app/
    │   ├── __init__.py
    │   ├── models.py
    │   ├── routes.py
    │   ├── forms.py
    │   ├── templates/
    │   │   ├── base.html
    │   │   ├── index.html
    │   │   ├── post.html
    │   │   └── admin/
    │   │       ├── edit_post.html
    │   │       └── new_post.html
    │   └── static/
    │       ├── css/
    │       │   └── style.css
    │       └── img/
    ├── migrations/
    ├── venv/
    ├── config.py
    └── run.py
    

    What Each File Does

    • run.py – Entry point that launches the Flask development server.
    • config.py – Central place for configuration variables (secret key, database URI, etc.).
    • app/__init__.py – Creates the Flask app, registers extensions, and loads blueprints.
    • app/models.py – Defines database models for Post and Comment.
    • app/routes.py – Contains view functions (URL routes) for the public and admin sections.
    • app/forms.py – WTForms classes for post creation and comment submission.
    • templates/ – Jinja2 HTML templates that render the UI.

    Step 2: Initialize the Flask Application

    Open app/__init__.py and add the following code:

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_migrate import Migrate
    from flask_wtf import CSRFProtect
    
    db = SQLAlchemy()
    migrate = Migrate()
    csrf = CSRFProtect()
    
    def create_app():
        app = Flask(__name__)
        app.config.from_object('config.Config')
    
        # Initialize extensions
        db.init_app(app)
        migrate.init_app(app, db)
        csrf.init_app(app)
    
        # Register blueprints
        from .routes import main_bp, admin_bp
        app.register_blueprint(main_bp)
        app.register_blueprint(admin_bp, url_prefix='/admin')
    
        return app
    

    And in run.py:

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

    Step 3: Configure the Application

    In config.py define a simple configuration class. For production you would use environment variables, but for this tutorial a static secret works fine.

    import os
    
    class Config:
        SECRET_KEY = os.getenv('SECRET_KEY', 'super-secret-key')
        SQLALCHEMY_DATABASE_URI = os.getenv(
            'DATABASE_URL',
            'sqlite:///blog.db'
        )
        SQLALCHEMY_TRACK_MODIFICATIONS = False
    

    Step 4: Create Database Models

    Open app/models.py and define the Post and Comment models. Include SEO‑relevant fields like slug and meta_description.

    from datetime import datetime
    from . import db
    
    class Post(db.Model):
        __tablename__ = 'posts'
        id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(150), nullable=False)
        slug = db.Column(db.String(150), unique=True, nullable=False)
        body = db.Column(db.Text, nullable=False)
        meta_description = db.Column(db.String(160))
        created_at = db.Column(db.DateTime, default=datetime.utcnow)
        updated_at = db.Column(db.DateTime, onupdate=datetime.utcnow)
        comments = db.relationship('Comment', backref='post', cascade='all, delete-orphan')
    
        def __repr__(self):
            return f''
    
    class Comment(db.Model):
        __tablename__ = 'comments'
        id = db.Column(db.Integer, primary_key=True)
        author = db.Column(db.String(80), nullable=False)
        content = db.Column(db.Text, nullable=False)
        created_at = db.Column(db.DateTime, default=datetime.utcnow)
        post_id = db.Column(db.Integer, db.ForeignKey('posts.id'), nullable=False)
    
        def __repr__(self):
            return f''
    

    Step 5: Build the Forms with WTForms

    WTForms makes handling user input safe and convenient. Add the following to app/forms.py:

    from flask_wtf import FlaskForm
    from wtforms import StringField, TextAreaField, SubmitField
    from wtforms.validators import DataRequired, Length
    
    class PostForm(FlaskForm):
        title = StringField('Title', validators=[DataRequired(), Length(max=150)])
        slug = StringField('Slug', validators=[DataRequired(), Length(max=150)])
        meta_description = StringField('Meta Description', validators=[Length(max=160)])
        body = TextAreaField('Content', validators=[DataRequired()])
        submit = SubmitField('Publish')
    
    class CommentForm(FlaskForm):
        author = StringField('Name', validators=[DataRequired(), Length(max=80)])
        content = TextAreaField('Comment', validators=[DataRequired()])
        submit = SubmitField('Add Comment')
    

    Step 6: Define Routes and Views

    We’ll split routes into two blueprints: one for public pages (main_bp) and one for admin actions (admin_bp). Create app/routes.py with the following content:

    from flask import Blueprint, render_template, redirect, url_for, flash, request, abort
    from . import db
    from .models import Post, Comment
    from .forms import PostForm, CommentForm
    from slugify import slugify

    main_bp = Blueprint('main', __name__)
    admin_bp = Blueprint('admin', __name__)

    # ---------- Public Routes ----------
    @main_bp.route('/')
    def index():
    posts = Post.query.order_by(Post.created_at.desc()).all()
    return render_template('index.html', posts=posts)

    @main_bp.route('/post/', methods=['GET', 'POST'])
    def post_detail(slug):
    post = Post.query.filter_by(slug=slug).first_or_404()
    form = CommentForm()
    if form.validate_on_submit():
    comment = Comment(
    author=form.author.data,
    content=form.content.data,
    post=post
    )
    db.session.add(comment)
    db.session.commit()
    flash('Your comment has been added.', 'success')
    return redirect(url_for('main.post_detail', slug=slug))
    return render_template('post.html', post=post, form=form)

    # ---------- Admin Routes ----------
    @admin_bp.route('/')
    def admin_dashboard():
    posts = Post.query.order_by(Post.created_at.desc()).all()
    return render_template('admin/dashboard.html', posts=posts)

    @admin_bp.route('/new', methods=['GET', 'POST'])
    def new_post():
    form = PostForm()
    if form.validate_on_submit():
    post = Post(
    title=form.title.data,
    slug=slugify(form.slug.data),
    meta_description=form.meta_description.data,
    body=form.body.data
    )
    db.session.add(post)
    db.session.commit()
    flash('Post created successfully!', 'success')
    return redirect(url_for('admin.admin_dashboard'))
    return render_template('admin/new_post.html', form=form)

    @admin_bp.route('/edit/', methods=['GET', 'POST'])
    def edit_post(post_id):
    post = Post.query.get_or_404(post_id)
    form = PostForm(obj=post)
    if form.validate_on_submit():
    post.title = form.title.data
    post.slug = slugify(form.slug.data)
    post.meta_description = form.meta_description.data
    post.body = form.body.data
    db.session.commit()
    flash('Post updated successfully.', 'success')
    return redirect(url_for('admin.admin_dashboard'))
    return render

  • Python Flask User Authentication System

    Building a secure and scalable user authentication system is one of the first milestones for any web application. With Python Flask, you get a lightweight framework that lets you craft a custom authentication flow while still benefiting from powerful extensions like Flask‑Login, Flask‑Bcrypt, and Flask‑SQLAlchemy. In this guide we’ll walk through the essential concepts, step‑by‑step code snippets, and best practices you need to create a robust Flask authentication system that’s both developer‑friendly and SEO‑optimized for the term “Python Flask user authentication system”.

    Why Choose Flask for Authentication?

    Flask’s modular design gives you full control over every layer of the authentication stack. Unlike monolithic frameworks, Flask lets you:

    • Pick only the tools you need – add Flask‑Login for session management, Flask‑Bcrypt for password hashing, and Flask‑Mail for email verification.
    • Keep the codebase clean – each component lives in its own module, making maintenance and testing straightforward.
    • Scale effortlessly – you can swap SQLite for PostgreSQL, or move from a simple file‑based session store to Redis without rewriting your authentication logic.

    Project Structure Overview

    A well‑organized project layout reduces friction when extending the authentication system later. Below is a recommended directory tree:

    my_flask_app/
    │
    ├─ app/
    │   ├─ __init__.py          # Application factory
    │   ├─ models.py            # SQLAlchemy models (User, Role, etc.)
    │   ├─ auth/
    │   │   ├─ __init__.py
    │   │   ├─ routes.py        # Login, logout, register endpoints
    │   │   └─ forms.py         # WTForms for validation
    │   └─ templates/
    │       └─ auth/
    │           ├─ login.html
    │           └─ register.html
    │
    ├─ migrations/              # Flask‑Migrate scripts
    ├─ requirements.txt
    └─ run.py                    # Entry point
    

    Step 1: Setting Up the Environment

    Install Required Packages

    • Flask
    • Flask‑SQLAlchemy
    • Flask‑Migrate
    • Flask‑Login
    • Flask‑Bcrypt
    • Flask‑WTForms
    • Flask‑Mail (optional, for email verification)

    Run the following command to install everything at once:

    pip install Flask Flask-SQLAlchemy Flask-Migrate Flask-Login Flask-Bcrypt Flask-WTF Flask-Mail

    Create the Application Factory

    The factory pattern keeps the app configurable for testing, development, and production.

    # app/__init__.py
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_migrate import Migrate
    from flask_login import LoginManager
    from flask_bcrypt import Bcrypt
    from flask_mail import Mail
    
    db = SQLAlchemy()
    migrate = Migrate()
    login_manager = LoginManager()
    bcrypt = Bcrypt()
    mail = Mail()
    
    def create_app(config_name='development'):
        app = Flask(__name__)
        app.config.from_object(f'config.{config_name.capitalize()}Config')
    
        # Initialise extensions
        db.init_app(app)
        migrate.init_app(app, db)
        login_manager.init_app(app)
        bcrypt.init_app(app)
        mail.init_app(app)
    
        login_manager.login_view = 'auth.login'
        login_manager.login_message_category = 'info'
    
        # Register blueprints
        from .auth import auth_bp
        app.register_blueprint(auth_bp)
    
        return app
    

    Step 2: Defining the User Model

    The User class stores credentials, status flags, and integrates with Flask‑Login.

    # app/models.py
    from . import db, bcrypt
    from flask_login import UserMixin
    from . import login_manager
    
    class User(UserMixin, db.Model):
        __tablename__ = 'users'
    
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(150), unique=True, nullable=False)
        email = db.Column(db.String(255), unique=True, nullable=False)
        password_hash = db.Column(db.String(128), nullable=False)
        is_active = db.Column(db.Boolean, default=True)
        created_at = db.Column(db.DateTime, server_default=db.func.now())
    
        def set_password(self, password):
            self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
    
        def check_password(self, password):
            return bcrypt.check_password_hash(self.password_hash, password)
    
    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))
    

    Step 3: Building Forms with WTForms

    Using Flask‑WTF gives us CSRF protection and clean validation.

    # app/auth/forms.py
    from flask_wtf import FlaskForm
    from wtforms import StringField, PasswordField, SubmitField, BooleanField
    from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
    from ..models import User
    
    class RegistrationForm(FlaskForm):
        username = StringField('Username', validators=[
            DataRequired(), Length(min=3, max=150)])
        email = StringField('Email', validators=[
            DataRequired(), Email()])
        password = PasswordField('Password', validators=[
            DataRequired(), Length(min=8)])
        confirm_password = PasswordField('Confirm Password', validators=[
            DataRequired(), EqualTo('password')])
        submit = SubmitField('Create Account')
    
        def validate_username(self, username):
            if User.query.filter_by(username=username.data).first():
                raise ValidationError('Username already taken.')
    
        def validate_email(self, email):
            if User.query.filter_by(email=email.data).first():
                raise ValidationError('Email already registered.')
    
    class LoginForm(FlaskForm):
        email = StringField('Email', validators=[DataRequired(), Email()])
        password = PasswordField('Password', validators=[DataRequired()])
        remember = BooleanField('Remember Me')
        submit = SubmitField('Log In')
    

    Step 4: Implementing Authentication Routes

    Register, Login, and Logout Endpoints

    # app/auth/routes.py
    from flask import Blueprint, render_template, redirect, url_for, flash, request
    from flask_login import login_user, logout_user, current_user, login_required
    from .. import db
    from ..models import User
    from .forms import RegistrationForm, LoginForm
    
    auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
    
    @auth_bp.route('/register', methods=['GET', 'POST'])
    def register():
        if current_user.is_authenticated:
            return redirect(url_for('main.index'))
        form = RegistrationForm()
        if form.validate_on_submit():
            user = User(username=form.username.data,
                        email=form.email.data)
            user.set_password(form.password.data)
            db.session.add(user)
            db.session.commit()
            flash('Your account has been created! You can now log in.', 'success')
            return redirect(url_for('auth.login'))
        return render_template('auth/register.html', form=form)
    
    @auth_bp.route('/login', methods=['GET', 'POST'])
    def login():
        if current_user.is_authenticated:
            return redirect(url_for('main.index'))
        form = LoginForm()
        if form.validate_on_submit():
            user = User.query.filter_by(email=form.email.data).first()
            if user and user.check_password(form.password.data):
                login_user(user, remember=form.remember.data)
                next_page = request.args.get('next')
                return redirect(next_page) if next_page else redirect(url_for('main.index'))
            else:
                flash('Login failed. Check email and password.', 'danger')
        return render_template('auth/login.html', form=form)
    
    @auth_bp.route('/logout')
    @login_required
    def logout():
        logout_user()
        flash('You have been logged out.', 'info')
        return redirect(url_for('auth.login'))
    

    Step 5: Securing Routes with @login_required

    Any view that should be accessible only to authenticated users must be wrapped with @login_required. Example:

    # app/main/routes.py
    from flask import Blueprint, render_template
    from flask_login import login_required, current_user
    
    main_bp = Blueprint('main', __name__)
    
    @main_bp.route('/')
    def index():
        return render_template('index.html')
    
    @main_bp.route('/dashboard')
    @login_required
    def dashboard():
        return render_template('dashboard.html', user=current_user)
    

    Step 6: Adding Email Confirmation (Optional but Recommended)

    To prevent fake accounts, send a verification link after registration. The workflow:

    1. User registers → generate a signed token (itsdangerous).
    2. Email the token link using Flask‑Mail.
    3. User clicks the link → verify token and activate the account.

    Here’s a concise implementation:

    # app/auth/email.py
    from flask_mail import Message
    from itsdangerous import URLSafeTimedSerializer
    from flask import current_app, url_for
    from .. import mail
    
    def generate_confirmation_token(email):
        serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
        return serializer.dumps(email, salt=current_app.config['SECURITY_PASSWORD_SALT'])
    
    def confirm_token(token, expiration=3600):
        serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
        try:
            email = serializer.loads(token, salt=current_app.config['SECURITY_PASSWORD_SALT'], max_age=expiration)
        except Exception:
            return False
        return email
    
    def send_confirmation_email(user):
        token = generate_confirmation_token(user.email)
        confirm_url = url_for('auth.confirm_email', token=token, _external=True)
        html = render_template('auth/activate.html', confirm_url=confirm_url, user=user)
        msg = Message('Please confirm your email', recipients=[user.email], html=html)
        mail.send(msg)
    

    Step 7: Configuring Security Settings

    Security is a moving target; keep these settings up‑to‑date in your