Загрузка данных


Вот обновленный код всех модулей без единого комментария, оформленный в строгом соответствии с вашими требованиями.
app.py
import os
import uuid
from werkzeug.utils import secure_filename
from flask import Flask, render_template, request, redirect, url_for, flash, abort

app = Flask(__name__)
app.secret_key = 'dev-key'

app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def save_image(file):
    if not file or file.filename == '':
        return None
    if not allowed_file(file.filename):
        return None
    
    ext = file.filename.rsplit('.', 1)[1].lower()
    unique_name = f'{uuid.uuid4().hex}.{ext}'
    path = os.path.join(app.config['UPLOAD_FOLDER'], unique_name)
    
    os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
    
    file.save(path)
    return unique_name

@app.route('/items/add', methods=['GET', 'POST'])
@login_required
def item_add():
    if request.method == 'POST':
        title = request.form.get('title', '').strip()
        description = request.form.get('description', '').strip()
        available = request.form.get('available') == 'on'
        
        if not title:
            flash('Введите название', 'error')
            return redirect(url_for('item_add'))
        
        file = request.files.get('image')
        image_name = save_image(file)
        if file and file.filename != '' and not image_name:
            flash('Недопустимый формат файла', 'error')
            return redirect(url_for('item_add'))
        
        item = Item(
            title=title,
            description=description,
            available=available,
            image=image_name,
        )
        db.session.add(item)
        db.session.commit()
        
        files = request.files.getlist('images')
        for f in files:
            img_name = save_image(f)
            if img_name:
                db.session.add(Image(filename=img_name, item_id=item.id))
        db.session.commit()
        
        flash('Элемент успешно добавлен', 'success')
        return redirect(url_for('item_list'))
        
    return render_template('item_form.html', item=None)

@app.route('/items/<int:item_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def item_edit(item_id):
    item = Item.query.get_or_404(item_id)
    if request.method == 'POST':
        title = request.form.get('title', '').strip()
        description = request.form.get('description', '').strip()
        available = request.form.get('available') == 'on'
        
        if not title:
            flash('Введите название', 'error')
            return redirect(url_for('item_edit', item_id=item.id))
        
        new_image = save_image(request.files.get('image'))
        if new_image:
            if item.image:
                old_path = os.path.join(app.config['UPLOAD_FOLDER'], item.image)
                if os.path.exists(old_path):
                    os.remove(old_path)
            item.image = new_image
            
        item.title = title
        item.description = description
        item.available = available
        
        files = request.files.getlist('images')
        for f in files:
            img_name = save_image(f)
            if img_name:
                db.session.add(Image(filename=img_name, item_id=item.id))
                
        db.session.commit()
        flash('Элемент обновлён', 'success')
        return redirect(url_for('item_detail', item_id=item.id))
        
    return render_template('item_form.html', item=item)

@app.route('/items/<int:item_id>/delete', methods=['POST'])
@login_required
@admin_required
def item_delete(item_id):
    item = Item.query.get_or_404(item_id)
    
    if item.image:
        path = os.path.join(app.config['UPLOAD_FOLDER'], item.image)
        if os.path.exists(path):
            os.remove(path)
            
    for img in item.images:
        path = os.path.join(app.config['UPLOAD_FOLDER'], img.filename)
        if os.path.exists(path):
            os.remove(path)
            
    db.session.delete(item)
    db.session.commit()
    
    flash('Элемент удалён', 'success')
    return redirect(url_for('item_list'))

@app.errorhandler(413)
def too_large(error):
    flash('Файл слишком большой (максимум 2 МБ)', 'error')
    return redirect(request.url)

models.py
class Item(db.Model):
    __tablename__ = 'items'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(150), nullable=False)
    description = db.Column(db.Text, nullable=True)
    available = db.Column(db.Boolean, default=True)
    image = db.Column(db.String(200), nullable=True)
    images = db.relationship('Image', backref='item', cascade='all, delete-orphan')

class Image(db.Model):
    __tablename__ = 'images'
    id = db.Column(db.Integer, primary_key=True)
    filename = db.Column(db.String(200), nullable=False)
    item_id = db.Column(db.Integer, db.ForeignKey('items.id'), nullable=False)

static/style.css
.thumb {
    width: 60px;
    height: 60px;
    object-fit: cover;
    border-radius: 6px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.no-image {
    color: #888;
    font-style: italic;
}

.detail-image {
    max-width: 100%;
    max-height: 400px;
    border-radius: 8px;
    object-fit: contain;
    margin-bottom: 20px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

.gallery {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 15px;
}

.gallery-item {
    width: 100px;
    height: 100px;
    object-fit: cover;
    border-radius: 6px;
    transition: transform 0.2s ease;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.gallery-item:hover {
    transform: scale(1.03);
}