📝 Todo App - SQLite bilan to'liq loyiha

Todo App loyihasi

Bu darsda SQLite3 ishlatgan holda to'liq Todo App loyihasini yaratamiz. Bu loyiha barcha o'rganilgan SQLite3 texnikalarini qo'llaydi.

1. Loyiha struktura

todo_app/
├── app.py              # Asosiy Flask app
├── database.py         # Ma'lumotlar bazasi funksiyalari
├── models.py           # Data modellar
├── templates/          # HTML template-lar
│   ├── base.html
│   ├── index.html
│   ├── add_task.html
│   └── edit_task.html
├── static/            # CSS va JS fayllar
│   ├── style.css
│   └── script.js
├── requirements.txt   # Dependencies
└── todo.db           # SQLite ma'lumotlar bazasi

2. Ma'lumotlar bazasi dizayni

# database.py
import sqlite3
from datetime import datetime

class TodoDatabase:
    def __init__(self, db_path='todo.db'):
        self.db_path = db_path
        self.init_database()
    
    def init_database(self):
        """Ma'lumotlar bazasini ishga tushirish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Tasks jadvali
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS tasks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title TEXT NOT NULL,
                description TEXT,
                priority TEXT DEFAULT 'medium',
                status TEXT DEFAULT 'pending',
                due_date DATE,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
            ''')
            
            # Categories jadvali
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS categories (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT UNIQUE NOT NULL,
                color TEXT DEFAULT '#007bff',
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
            ''')
            
            # Task categories jadvali
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS task_categories (
                task_id INTEGER,
                category_id INTEGER,
                PRIMARY KEY (task_id, category_id),
                FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
                FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
            )
            ''')
            
            # Foreign Key yoqish
            cursor.execute('PRAGMA foreign_keys = ON')
            
            # Default categories qo'shish
            cursor.execute('''
            INSERT OR IGNORE INTO categories (name, color) VALUES 
            ('Work', '#28a745'),
            ('Personal', '#007bff'),
            ('Shopping', '#ffc107'),
            ('Health', '#dc3545')
            ''')
            
            conn.commit()
    
    def create_task(self, title, description=None, priority='medium', due_date=None, category_ids=None):
        """Yangi task yaratish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('''
            INSERT INTO tasks (title, description, priority, due_date) 
            VALUES (?, ?, ?, ?)
            ''', (title, description, priority, due_date))
            
            task_id = cursor.lastrowid
            
            # Categories qo'shish
            if category_ids:
                for category_id in category_ids:
                    cursor.execute('''
                    INSERT INTO task_categories (task_id, category_id) 
                    VALUES (?, ?)
                    ''', (task_id, category_id))
            
            conn.commit()
            return task_id
    
    def get_all_tasks(self, status=None, priority=None, category_id=None):
        """Barcha tasklarni olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            query = '''
            SELECT t.id, t.title, t.description, t.priority, t.status, 
                   t.due_date, t.created_at, t.updated_at,
                   GROUP_CONCAT(c.name) as categories
            FROM tasks t
            LEFT JOIN task_categories tc ON t.id = tc.task_id
            LEFT JOIN categories c ON tc.category_id = c.id
            WHERE 1=1
            '''
            
            params = []
            
            if status:
                query += ' AND t.status = ?'
                params.append(status)
            
            if priority:
                query += ' AND t.priority = ?'
                params.append(priority)
            
            if category_id:
                query += ' AND t.id IN (SELECT task_id FROM task_categories WHERE category_id = ?)'
                params.append(category_id)
            
            query += ' GROUP BY t.id ORDER BY t.created_at DESC'
            
            cursor.execute(query, params)
            return cursor.fetchall()
    
    def get_task_by_id(self, task_id):
        """Task ma'lumotlarini olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('''
            SELECT t.id, t.title, t.description, t.priority, t.status, 
                   t.due_date, t.created_at, t.updated_at
            FROM tasks t
            WHERE t.id = ?
            ''', (task_id,))
            
            task = cursor.fetchone()
            
            if task:
                # Categories olish
                cursor.execute('''
                SELECT c.id, c.name, c.color
                FROM categories c
                INNER JOIN task_categories tc ON c.id = tc.category_id
                WHERE tc.task_id = ?
                ''', (task_id,))
                
                categories = cursor.fetchall()
                return task, categories
            
            return None, []
    
    def update_task(self, task_id, title=None, description=None, priority=None, 
                   status=None, due_date=None, category_ids=None):
        """Task ma'lumotlarini yangilash"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Yangilanish maydonlarini aniqlash
            updates = []
            params = []
            
            if title is not None:
                updates.append('title = ?')
                params.append(title)
            
            if description is not None:
                updates.append('description = ?')
                params.append(description)
            
            if priority is not None:
                updates.append('priority = ?')
                params.append(priority)
            
            if status is not None:
                updates.append('status = ?')
                params.append(status)
            
            if due_date is not None:
                updates.append('due_date = ?')
                params.append(due_date)
            
            if updates:
                updates.append('updated_at = CURRENT_TIMESTAMP')
                params.append(task_id)
                
                query = f"UPDATE tasks SET {', '.join(updates)} WHERE id = ?"
                cursor.execute(query, params)
            
            # Categories yangilash
            if category_ids is not None:
                # Eski categories o'chirish
                cursor.execute('DELETE FROM task_categories WHERE task_id = ?', (task_id,))
                
                # Yangi categories qo'shish
                for category_id in category_ids:
                    cursor.execute('''
                    INSERT INTO task_categories (task_id, category_id) 
                    VALUES (?, ?)
                    ''', (task_id, category_id))
            
            conn.commit()
            return cursor.rowcount > 0
    
    def delete_task(self, task_id):
        """Task o'chirish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('DELETE FROM tasks WHERE id = ?', (task_id,))
            conn.commit()
            return cursor.rowcount > 0
    
    def get_categories(self):
        """Barcha categories olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('SELECT * FROM categories ORDER BY name')
            return cursor.fetchall()
    
    def create_category(self, name, color):
        """Yangi category yaratish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('''
            INSERT INTO categories (name, color) VALUES (?, ?)
            ''', (name, color))
            conn.commit()
            return cursor.lastrowid
    
    def get_task_statistics(self):
        """Task statistikasi"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Umumiy statistika
            cursor.execute('''
            SELECT 
                COUNT(*) as total_tasks,
                SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_tasks,
                SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_tasks,
                SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_tasks
            FROM tasks
            ''')
            
            stats = cursor.fetchone()
            
            # Priority bo'yicha statistika
            cursor.execute('''
            SELECT priority, COUNT(*) as count
            FROM tasks
            GROUP BY priority
            ''')
            
            priority_stats = cursor.fetchall()
            
            # Category bo'yicha statistika
            cursor.execute('''
            SELECT c.name, COUNT(tc.task_id) as count
            FROM categories c
            LEFT JOIN task_categories tc ON c.id = tc.category_id
            GROUP BY c.id, c.name
            ORDER BY count DESC
            ''')
            
            category_stats = cursor.fetchall()
            
            return {
                'total': stats[0],
                'pending': stats[1],
                'completed': stats[2],
                'cancelled': stats[3],
                'priority_stats': priority_stats,
                'category_stats': category_stats
            }

3. Flask App

# app.py
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from database import TodoDatabase
from datetime import datetime

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'

# Database instance
db = TodoDatabase()

@app.route('/')
def index():
    """Asosiy sahifa"""
    # Filter parametrlari
    status = request.args.get('status')
    priority = request.args.get('priority')
    category_id = request.args.get('category_id')
    
    # Tasklarni olish
    tasks = db.get_all_tasks(status=status, priority=priority, category_id=category_id)
    
    # Categories olish
    categories = db.get_categories()
    
    # Statistika
    stats = db.get_task_statistics()
    
    return render_template('index.html', 
                         tasks=tasks, 
                         categories=categories, 
                         stats=stats,
                         current_filters={
                             'status': status,
                             'priority': priority,
                             'category_id': category_id
                         })

@app.route('/add', methods=['GET', 'POST'])
def add_task():
    """Yangi task qo'shish"""
    if request.method == 'POST':
        title = request.form['title']
        description = request.form.get('description', '')
        priority = request.form.get('priority', 'medium')
        due_date = request.form.get('due_date')
        category_ids = request.form.getlist('categories')
        
        # Category IDs ni integer ga o'tkazish
        category_ids = [int(cid) for cid in category_ids if cid]
        
        # Task yaratish
        task_id = db.create_task(
            title=title,
            description=description,
            priority=priority,
            due_date=due_date,
            category_ids=category_ids
        )
        
        flash('Task muvaffaqiyatli yaratildi!', 'success')
        return redirect(url_for('index'))
    
    # Categories olish
    categories = db.get_categories()
    return render_template('add_task.html', categories=categories)

@app.route('/edit/', methods=['GET', 'POST'])
def edit_task(task_id):
    """Task tahrirlash"""
    if request.method == 'POST':
        title = request.form['title']
        description = request.form.get('description', '')
        priority = request.form.get('priority', 'medium')
        status = request.form.get('status', 'pending')
        due_date = request.form.get('due_date')
        category_ids = request.form.getlist('categories')
        
        # Category IDs ni integer ga o'tkazish
        category_ids = [int(cid) for cid in category_ids if cid]
        
        # Task yangilash
        success = db.update_task(
            task_id=task_id,
            title=title,
            description=description,
            priority=priority,
            status=status,
            due_date=due_date,
            category_ids=category_ids
        )
        
        if success:
            flash('Task muvaffaqiyatli yangilandi!', 'success')
        else:
            flash('Task yangilanmadi!', 'error')
        
        return redirect(url_for('index'))
    
    # Task ma'lumotlarini olish
    task_data = db.get_task_by_id(task_id)
    if not task_data:
        flash('Task topilmadi!', 'error')
        return redirect(url_for('index'))
    
    task, task_categories = task_data
    
    # Categories olish
    categories = db.get_categories()
    
    return render_template('edit_task.html', 
                         task=task, 
                         categories=categories,
                         task_categories=task_categories)

@app.route('/delete/')
def delete_task(task_id):
    """Task o'chirish"""
    success = db.delete_task(task_id)
    
    if success:
        flash('Task muvaffaqiyatli o''chirildi!', 'success')
    else:
        flash('Task o''chirilmadi!', 'error')
    
    return redirect(url_for('index'))

@app.route('/complete/')
def complete_task(task_id):
    """Task ni tugatish"""
    success = db.update_task(task_id, status='completed')
    
    if success:
        flash('Task tugatildi!', 'success')
    else:
        flash('Task tugatilmadi!', 'error')
    
    return redirect(url_for('index'))

@app.route('/api/tasks')
def api_tasks():
    """API - Tasklar"""
    status = request.args.get('status')
    priority = request.args.get('priority')
    category_id = request.args.get('category_id')
    
    tasks = db.get_all_tasks(status=status, priority=priority, category_id=category_id)
    
    # JSON formatga o'tkazish
    tasks_data = []
    for task in tasks:
        tasks_data.append({
            'id': task[0],
            'title': task[1],
            'description': task[2],
            'priority': task[3],
            'status': task[4],
            'due_date': task[5],
            'created_at': task[6],
            'updated_at': task[7],
            'categories': task[8] if task[8] else ''
        })
    
    return jsonify(tasks_data)

@app.route('/api/statistics')
def api_statistics():
    """API - Statistika"""
    stats = db.get_task_statistics()
    return jsonify(stats)

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

4. HTML Templates





    
    
    {% block title %}Todo App{% endblock %}
    
    
    


    

    
{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %}
{{ message }}
{% endfor %} {% endif %} {% endwith %} {% block content %}{% endblock %}
{% block scripts %}{% endblock %} {% extends "base.html" %} {% block title %}Todo App - Bosh sahifa{% endblock %} {% block content %}

Mening tasklarim

Yangi task
{% if tasks %} {% for task in tasks %}
{{ task[1] }} {{ task[3].title() }} {{ task[4].title() }}
{% if task[2] %}

{{ task[2] }}

{% endif %} {% if task[8] %}

{{ task[8] }}

{% endif %} {% if task[5] %}

{{ task[5] }}

{% endif %}
{% if task[4] != 'completed' %} {% endif %}
{% endfor %} {% else %}

Hozircha task yo'q

Yangi task qo'shish uchun tugmani bosing

Yangi task
{% endif %}
Statistika

{{ stats.total }}

Jami

{{ stats.pending }}

Kutilmoqda

{{ stats.completed }}

Tugatilgan
Kategoriyalar
{% for category in categories %}
{{ category[1] }} {{ category[3] if category[3] else 0 }} task
{% endfor %}
{% endblock %}

5. Loyihani ishga tushirish

# requirements.txt
Flask==2.3.3
Werkzeug==2.3.7

# Loyihani ishga tushirish
pip install -r requirements.txt
python app.py

# Brauzerda ochish
# http://localhost:5000

6. Loyiha xususiyatlari