Initial commit
This commit is contained in:
6
.env.example
Normal file
6
.env.example
Normal file
@@ -0,0 +1,6 @@
|
||||
# Supabase Configuration
|
||||
# Получите эти данные из вашего проекта Supabase:
|
||||
# Settings -> API -> Project URL и Project API keys
|
||||
|
||||
SUPABASE_URL=your_supabase_project_url
|
||||
SUPABASE_KEY=your_supabase_anon_key
|
||||
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Distribution / packaging
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 [Your Name]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
343
README.md
Normal file
343
README.md
Normal file
@@ -0,0 +1,343 @@
|
||||
# Supabase Connector
|
||||
|
||||
Простой и удобный Python коннектор для работы с [Supabase](https://supabase.com).
|
||||
|
||||
## 🎯 О проекте
|
||||
|
||||
Этот проект предоставляет удобную обертку над официальным Python клиентом Supabase, упрощая базовые операции с базой данных и Storage.
|
||||
|
||||
## ✨ Возможности
|
||||
|
||||
- ✅ Простое подключение к Supabase через переменные окружения
|
||||
- 📊 CRUD операции (Create, Read, Update, Delete)
|
||||
- 🔍 Фильтрация и лимитирование результатов
|
||||
- 📦 Работа с Supabase Storage
|
||||
- 🛡️ Обработка ошибок
|
||||
- 🎨 Чистый и понятный API
|
||||
|
||||
## 📋 Требования
|
||||
|
||||
- Python 3.8+
|
||||
- Аккаунт Supabase (бесплатный или платный)
|
||||
|
||||
## 🚀 Установка
|
||||
|
||||
### Вариант 1: Клонирование репозитория
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yourusername/supabase-connector.git
|
||||
cd supabase-connector
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Вариант 2: Установка как пакет (в разработке)
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## ⚙️ Настройка
|
||||
|
||||
1. Создайте файл `.env` в корне проекта:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Заполните данными из вашего Supabase проекта:
|
||||
|
||||
```env
|
||||
SUPABASE_URL=https://your-project-id.supabase.co
|
||||
SUPABASE_KEY=your_anon_key
|
||||
```
|
||||
|
||||
**Где найти эти данные:**
|
||||
1. Откройте [Supabase Dashboard](https://app.supabase.com)
|
||||
2. Выберите ваш проект
|
||||
3. Перейдите в Settings → API
|
||||
4. Скопируйте **Project URL** и **anon/public key**
|
||||
|
||||
## 📖 Использование
|
||||
|
||||
### Базовый пример
|
||||
|
||||
```python
|
||||
from supabase import SupabaseManager
|
||||
|
||||
# Инициализация
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Получение данных
|
||||
users = sb.select(table="users", limit=10)
|
||||
print(f"Найдено пользователей: {len(users)}")
|
||||
|
||||
# Добавление записи
|
||||
new_user = sb.insert(
|
||||
table="users",
|
||||
data={
|
||||
"name": "Тимур",
|
||||
"email": "timur@example.com",
|
||||
"age": 28
|
||||
}
|
||||
)
|
||||
print(f"Создан пользователь с ID: {new_user.get('id')}")
|
||||
```
|
||||
|
||||
### Фильтрация данных
|
||||
|
||||
```python
|
||||
# Получить пользователей с определенным возрастом
|
||||
users_30 = sb.select(
|
||||
table="users",
|
||||
filters={"age": 30}
|
||||
)
|
||||
|
||||
# Получить только имена и email
|
||||
users = sb.select(
|
||||
table="users",
|
||||
columns="name, email",
|
||||
limit=5
|
||||
)
|
||||
```
|
||||
|
||||
### Обновление записей
|
||||
|
||||
```python
|
||||
# Обновить возраст пользователя с ID=1
|
||||
updated = sb.update(
|
||||
table="users",
|
||||
data={"age": 31},
|
||||
filters={"id": 1}
|
||||
)
|
||||
```
|
||||
|
||||
### Удаление записей
|
||||
|
||||
```python
|
||||
# Удалить пользователя с ID=1
|
||||
deleted = sb.delete(
|
||||
table="users",
|
||||
filters={"id": 1}
|
||||
)
|
||||
```
|
||||
|
||||
### Работа с Storage
|
||||
|
||||
```python
|
||||
# Загрузить файл
|
||||
with open("avatar.png", "rb") as f:
|
||||
file_data = f.read()
|
||||
|
||||
sb.upload_file(
|
||||
bucket="avatars",
|
||||
file_path="user_123/avatar.png",
|
||||
file_data=file_data,
|
||||
content_type="image/png"
|
||||
)
|
||||
|
||||
# Получить публичный URL
|
||||
url = sb.get_public_url(
|
||||
bucket="avatars",
|
||||
file_path="user_123/avatar.png"
|
||||
)
|
||||
print(f"Файл доступен по адресу: {url}")
|
||||
```
|
||||
|
||||
### Продвинутые запросы
|
||||
|
||||
Для сложных запросов используйте прямой доступ к клиенту:
|
||||
|
||||
```python
|
||||
client = sb.get_client()
|
||||
|
||||
# Запрос с операторами сравнения
|
||||
adults = client.table("users")\
|
||||
.select("*")\
|
||||
.gte("age", 18)\
|
||||
.order("age", desc=True)\
|
||||
.execute()
|
||||
|
||||
# Поиск по паттерну
|
||||
ivan_users = client.table("users")\
|
||||
.select("*")\
|
||||
.ilike("name", "%иван%")\
|
||||
.execute()
|
||||
```
|
||||
|
||||
## 📁 Структура проекта
|
||||
|
||||
```
|
||||
supabase-connector/
|
||||
├── supabase.py # Основной класс SupabaseManager
|
||||
├── __init__.py # Инициализация пакета
|
||||
├── example.py # Примеры использования
|
||||
├── requirements.txt # Зависимости
|
||||
├── setup.py # Настройка пакета
|
||||
├── .env.example # Шаблон конфигурации
|
||||
├── .gitignore # Исключения для git
|
||||
└── README.md # Документация
|
||||
```
|
||||
|
||||
## 🧪 Запуск примеров
|
||||
|
||||
В файле `example.py` находятся готовые примеры использования:
|
||||
|
||||
```bash
|
||||
python example.py
|
||||
```
|
||||
|
||||
Раскомментируйте нужные примеры в функции `main()`.
|
||||
|
||||
## 📝 API Reference
|
||||
|
||||
### SupabaseManager
|
||||
|
||||
#### `__init__()`
|
||||
Инициализирует подключение к Supabase, используя переменные окружения.
|
||||
|
||||
#### `select(table, columns="*", filters=None, limit=None)`
|
||||
Выполняет SELECT запрос.
|
||||
|
||||
**Параметры:**
|
||||
- `table` (str): Название таблицы
|
||||
- `columns` (str): Колонки для выборки
|
||||
- `filters` (dict): Словарь с фильтрами `{column: value}`
|
||||
- `limit` (int): Максимальное количество записей
|
||||
|
||||
**Возвращает:** `List[Dict]`
|
||||
|
||||
#### `insert(table, data)`
|
||||
Вставляет новую запись.
|
||||
|
||||
**Параметры:**
|
||||
- `table` (str): Название таблицы
|
||||
- `data` (dict): Данные для вставки
|
||||
|
||||
**Возвращает:** `Dict`
|
||||
|
||||
#### `update(table, data, filters)`
|
||||
Обновляет записи.
|
||||
|
||||
**Параметры:**
|
||||
- `table` (str): Название таблицы
|
||||
- `data` (dict): Данные для обновления
|
||||
- `filters` (dict): Фильтры `{column: value}`
|
||||
|
||||
**Возвращает:** `List[Dict]`
|
||||
|
||||
#### `delete(table, filters)`
|
||||
Удаляет записи.
|
||||
|
||||
**Параметры:**
|
||||
- `table` (str): Название таблицы
|
||||
- `filters` (dict): Фильтры `{column: value}`
|
||||
|
||||
**Возвращает:** `List[Dict]`
|
||||
|
||||
#### `upload_file(bucket, file_path, file_data, content_type=None)`
|
||||
Загружает файл в Storage.
|
||||
|
||||
**Параметры:**
|
||||
- `bucket` (str): Название bucket
|
||||
- `file_path` (str): Путь к файлу в bucket
|
||||
- `file_data` (bytes): Данные файла
|
||||
- `content_type` (str): MIME тип файла
|
||||
|
||||
**Возвращает:** `bool`
|
||||
|
||||
#### `get_public_url(bucket, file_path)`
|
||||
Получает публичный URL файла.
|
||||
|
||||
**Параметры:**
|
||||
- `bucket` (str): Название bucket
|
||||
- `file_path` (str): Путь к файлу
|
||||
|
||||
**Возвращает:** `str`
|
||||
|
||||
#### `get_client()`
|
||||
Возвращает прямой доступ к клиенту Supabase для сложных операций.
|
||||
|
||||
**Возвращает:** `Client`
|
||||
|
||||
## 🧪 Создание тестовой таблицы
|
||||
|
||||
Для тестирования создайте таблицу в Supabase:
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
age INTEGER,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Тестовые данные
|
||||
INSERT INTO users (name, email, age) VALUES
|
||||
('Иван Иванов', 'ivan@example.com', 30),
|
||||
('Мария Петрова', 'maria@example.com', 25),
|
||||
('Алексей Сидоров', 'alex@example.com', 35);
|
||||
```
|
||||
|
||||
## 🔐 Безопасность
|
||||
|
||||
- ⚠️ Никогда не коммитьте файл `.env` в git
|
||||
- Используйте **anon key** для клиентских операций
|
||||
- Настройте Row Level Security (RLS) в Supabase
|
||||
- Для серверных операций используйте **service_role key** с осторожностью
|
||||
|
||||
## 🛠️ Расширение функциональности
|
||||
|
||||
Вы можете легко добавить свои методы в класс `SupabaseManager`:
|
||||
|
||||
```python
|
||||
class SupabaseManager:
|
||||
# ... существующие методы ...
|
||||
|
||||
def count_records(self, table: str) -> int:
|
||||
"""Подсчет количества записей в таблице"""
|
||||
data = self.select(table=table)
|
||||
return len(data)
|
||||
|
||||
def search_by_text(self, table: str, column: str, search_term: str):
|
||||
"""Полнотекстовый поиск"""
|
||||
return self.client.table(table)\
|
||||
.select("*")\
|
||||
.ilike(column, f"%{search_term}%")\
|
||||
.execute().data
|
||||
```
|
||||
|
||||
## 🐛 Решение проблем
|
||||
|
||||
### Ошибка подключения
|
||||
```
|
||||
ValueError: SUPABASE_URL и SUPABASE_KEY должны быть установлены
|
||||
```
|
||||
**Решение:** Проверьте файл `.env` и убедитесь, что указаны правильные значения.
|
||||
|
||||
### Ошибка доступа к таблице
|
||||
```
|
||||
Ошибка при выполнении SELECT: ...
|
||||
```
|
||||
**Решение:**
|
||||
1. Убедитесь, что таблица существует
|
||||
2. Проверьте настройки RLS (Row Level Security)
|
||||
3. Убедитесь, что у вашего ключа есть доступ к таблице
|
||||
|
||||
## 📚 Дополнительные ресурсы
|
||||
|
||||
- [Официальная документация Supabase](https://supabase.com/docs)
|
||||
- [Supabase Python Client](https://supabase.com/docs/reference/python/introduction)
|
||||
- [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security)
|
||||
|
||||
## 🤝 Вклад в проект
|
||||
|
||||
Приветствуются любые предложения и улучшения! Создавайте Issues и Pull Requests.
|
||||
|
||||
## 📄 Лицензия
|
||||
|
||||
MIT License - используйте свободно в своих проектах.
|
||||
|
||||
## ⭐ Поддержка
|
||||
|
||||
Если проект был полезен, поставьте звезду на GitHub!
|
||||
8
__init__.py
Normal file
8
__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
Supabase Connector - простой Python коннектор для Supabase
|
||||
"""
|
||||
|
||||
from .supabase import SupabaseManager
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__all__ = ["SupabaseManager"]
|
||||
240
example.py
Normal file
240
example.py
Normal file
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Примеры использования SupabaseManager
|
||||
"""
|
||||
|
||||
from supabase import SupabaseManager
|
||||
|
||||
def example_select():
|
||||
"""Пример выборки данных из таблицы"""
|
||||
print("\n=== Пример SELECT ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Получить все записи
|
||||
all_users = sb.select(table="users")
|
||||
print(f"Всего пользователей: {len(all_users)}")
|
||||
|
||||
# Получить с фильтром
|
||||
filtered_users = sb.select(
|
||||
table="users",
|
||||
filters={"age": 30}
|
||||
)
|
||||
print(f"Пользователей с возрастом 30: {len(filtered_users)}")
|
||||
|
||||
# Получить с лимитом
|
||||
limited_users = sb.select(
|
||||
table="users",
|
||||
limit=5
|
||||
)
|
||||
print(f"Первые 5 пользователей: {len(limited_users)}")
|
||||
|
||||
# Выбрать только определенные колонки
|
||||
names_only = sb.select(
|
||||
table="users",
|
||||
columns="id, name"
|
||||
)
|
||||
for user in names_only:
|
||||
print(f" - {user.get('name')} (ID: {user.get('id')})")
|
||||
|
||||
|
||||
def example_insert():
|
||||
"""Пример добавления новой записи"""
|
||||
print("\n=== Пример INSERT ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
|
||||
new_user = {
|
||||
"name": "Тимур Иванов",
|
||||
"email": f"timur_{hash('test')}@example.com",
|
||||
"age": 28
|
||||
}
|
||||
|
||||
result = sb.insert(table="users", data=new_user)
|
||||
|
||||
if result:
|
||||
print(f"Создан пользователь: {result.get('name')} с ID {result.get('id')}")
|
||||
else:
|
||||
print("Ошибка при создании пользователя")
|
||||
|
||||
|
||||
def example_update():
|
||||
"""Пример обновления записи"""
|
||||
print("\n=== Пример UPDATE ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Сначала получим ID первого пользователя
|
||||
users = sb.select(table="users", limit=1)
|
||||
|
||||
if users:
|
||||
user_id = users[0].get('id')
|
||||
|
||||
# Обновим возраст
|
||||
updated = sb.update(
|
||||
table="users",
|
||||
data={"age": 31},
|
||||
filters={"id": user_id}
|
||||
)
|
||||
|
||||
if updated:
|
||||
print(f"Обновлен пользователь ID {user_id}: {updated[0]}")
|
||||
else:
|
||||
print("Нет пользователей для обновления")
|
||||
|
||||
|
||||
def example_delete():
|
||||
"""Пример удаления записи"""
|
||||
print("\n=== Пример DELETE ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Получим последнюю запись
|
||||
users = sb.select(table="users")
|
||||
|
||||
if users:
|
||||
last_user = users[-1]
|
||||
user_id = last_user.get('id')
|
||||
|
||||
print(f"Удаляем пользователя: {last_user.get('name')} (ID: {user_id})")
|
||||
|
||||
deleted = sb.delete(
|
||||
table="users",
|
||||
filters={"id": user_id}
|
||||
)
|
||||
|
||||
if deleted:
|
||||
print(f"✅ Пользователь удален")
|
||||
else:
|
||||
print("Нет пользователей для удаления")
|
||||
|
||||
|
||||
def example_complex_query():
|
||||
"""Пример работы с прямым клиентом Supabase для сложных запросов"""
|
||||
print("\n=== Пример сложных запросов ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
client = sb.get_client()
|
||||
|
||||
# Использование операторов сравнения
|
||||
adults = client.table("users")\
|
||||
.select("*")\
|
||||
.gte("age", 18)\
|
||||
.execute()
|
||||
|
||||
print(f"Взрослых пользователей (18+): {len(adults.data)}")
|
||||
|
||||
# Поиск по паттерну (LIKE)
|
||||
ivan_users = client.table("users")\
|
||||
.select("*")\
|
||||
.ilike("name", "%иван%")\
|
||||
.execute()
|
||||
|
||||
print(f"Пользователей с 'иван' в имени: {len(ivan_users.data)}")
|
||||
|
||||
# Сортировка
|
||||
sorted_users = client.table("users")\
|
||||
.select("*")\
|
||||
.order("age", desc=True)\
|
||||
.limit(3)\
|
||||
.execute()
|
||||
|
||||
print("Топ-3 самых старших пользователей:")
|
||||
for user in sorted_users.data:
|
||||
print(f" - {user.get('name')}, {user.get('age')} лет")
|
||||
|
||||
|
||||
def example_storage():
|
||||
"""Пример работы с Storage (загрузка файлов)"""
|
||||
print("\n=== Пример работы с Storage ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Создадим тестовый текстовый файл
|
||||
test_content = "Это тестовый файл для демонстрации работы с Supabase Storage"
|
||||
file_data = test_content.encode('utf-8')
|
||||
|
||||
# Загрузим файл
|
||||
success = sb.upload_file(
|
||||
bucket="test-bucket",
|
||||
file_path="examples/test.txt",
|
||||
file_data=file_data,
|
||||
content_type="text/plain"
|
||||
)
|
||||
|
||||
if success:
|
||||
# Получим публичный URL
|
||||
url = sb.get_public_url(
|
||||
bucket="test-bucket",
|
||||
file_path="examples/test.txt"
|
||||
)
|
||||
print(f"Файл доступен по URL: {url}")
|
||||
else:
|
||||
print("Ошибка при загрузке файла (проверьте, существует ли bucket 'test-bucket')")
|
||||
|
||||
|
||||
def example_batch_operations():
|
||||
"""Пример пакетных операций"""
|
||||
print("\n=== Пример пакетных операций ===")
|
||||
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Добавим несколько пользователей
|
||||
users_to_add = [
|
||||
{"name": "Алексей Петров", "email": f"alex_{i}@example.com", "age": 25 + i}
|
||||
for i in range(3)
|
||||
]
|
||||
|
||||
print(f"Добавляем {len(users_to_add)} пользователей...")
|
||||
|
||||
for user_data in users_to_add:
|
||||
result = sb.insert(table="users", data=user_data)
|
||||
if result:
|
||||
print(f" ✅ {result.get('name')}")
|
||||
|
||||
|
||||
def example_error_handling():
|
||||
"""Пример обработки ошибок"""
|
||||
print("\n=== Пример обработки ошибок ===")
|
||||
|
||||
try:
|
||||
sb = SupabaseManager()
|
||||
|
||||
# Попытка запроса к несуществующей таблице
|
||||
result = sb.select(table="nonexistent_table")
|
||||
|
||||
if not result:
|
||||
print("⚠️ Таблица не существует или запрос вернул пустой результат")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Произошла ошибка: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Запуск всех примеров"""
|
||||
print("=" * 60)
|
||||
print("Примеры использования SupabaseManager")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# Раскомментируйте нужные примеры
|
||||
|
||||
example_select()
|
||||
# example_insert()
|
||||
# example_update()
|
||||
# example_delete()
|
||||
# example_complex_query()
|
||||
# example_storage()
|
||||
# example_batch_operations()
|
||||
# example_error_handling()
|
||||
|
||||
except ValueError as e:
|
||||
print(f"\n❌ Ошибка инициализации: {e}")
|
||||
print("\n💡 Убедитесь, что вы создали файл .env с переменными:")
|
||||
print(" SUPABASE_URL=your_url")
|
||||
print(" SUPABASE_KEY=your_key")
|
||||
except Exception as e:
|
||||
print(f"\n❌ Непредвиденная ошибка: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
supabase>=2.0.0
|
||||
python-dotenv>=1.0.0
|
||||
39
setup.py
Normal file
39
setup.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
with open("README.md", "r", encoding="utf-8") as fh:
|
||||
long_description = fh.read()
|
||||
|
||||
setup(
|
||||
name="supabase-connector",
|
||||
version="1.0.0",
|
||||
author="Your Name",
|
||||
author_email="your.email@example.com",
|
||||
description="Простой Python коннектор для работы с Supabase",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/yourusername/supabase-connector",
|
||||
packages=find_packages(),
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
],
|
||||
python_requires=">=3.8",
|
||||
install_requires=[
|
||||
"supabase>=2.0.0",
|
||||
"python-dotenv>=1.0.0",
|
||||
],
|
||||
extras_require={
|
||||
"dev": [
|
||||
"pytest>=7.0.0",
|
||||
"black>=22.0.0",
|
||||
"flake8>=4.0.0",
|
||||
],
|
||||
},
|
||||
)
|
||||
BIN
streamlit_supabase_project.tar.gz
Normal file
BIN
streamlit_supabase_project.tar.gz
Normal file
Binary file not shown.
212
supabase.py
Normal file
212
supabase.py
Normal file
@@ -0,0 +1,212 @@
|
||||
import os
|
||||
from typing import Optional, Dict, List, Any
|
||||
from supabase import create_client, Client
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Загружаем переменные окружения
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class SupabaseManager:
|
||||
"""
|
||||
Класс для управления подключением и операциями с Supabase
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Инициализация подключения к Supabase
|
||||
"""
|
||||
self.url: str = os.getenv("SUPABASE_URL", "")
|
||||
self.key: str = os.getenv("SUPABASE_KEY", "")
|
||||
self.client: Optional[Client] = None
|
||||
|
||||
if not self.url or not self.key:
|
||||
raise ValueError(
|
||||
"SUPABASE_URL и SUPABASE_KEY должны быть установлены в переменных окружения"
|
||||
)
|
||||
|
||||
self._connect()
|
||||
|
||||
def _connect(self) -> None:
|
||||
"""
|
||||
Создает подключение к Supabase
|
||||
"""
|
||||
try:
|
||||
self.client = create_client(self.url, self.key)
|
||||
print("✅ Успешное подключение к Supabase")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка подключения к Supabase: {e}")
|
||||
raise
|
||||
|
||||
def get_client(self) -> Client:
|
||||
"""
|
||||
Возвращает клиент Supabase
|
||||
|
||||
Returns:
|
||||
Client: Клиент Supabase
|
||||
"""
|
||||
if not self.client:
|
||||
raise ConnectionError("Клиент Supabase не инициализирован")
|
||||
return self.client
|
||||
|
||||
# === Методы для работы с данными ===
|
||||
|
||||
def select(
|
||||
self,
|
||||
table: str,
|
||||
columns: str = "*",
|
||||
filters: Optional[Dict[str, Any]] = None,
|
||||
limit: Optional[int] = None
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Выполняет SELECT запрос к таблице
|
||||
|
||||
Args:
|
||||
table: Название таблицы
|
||||
columns: Колонки для выборки (по умолчанию все)
|
||||
filters: Словарь с фильтрами {column: value}
|
||||
limit: Лимит количества записей
|
||||
|
||||
Returns:
|
||||
List[Dict]: Список словарей с данными
|
||||
"""
|
||||
try:
|
||||
query = self.client.table(table).select(columns)
|
||||
|
||||
if filters:
|
||||
for column, value in filters.items():
|
||||
query = query.eq(column, value)
|
||||
|
||||
if limit:
|
||||
query = query.limit(limit)
|
||||
|
||||
response = query.execute()
|
||||
return response.data
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при выполнении SELECT: {e}")
|
||||
return []
|
||||
|
||||
def insert(self, table: str, data: Dict[str, Any]) -> Dict:
|
||||
"""
|
||||
Вставляет запись в таблицу
|
||||
|
||||
Args:
|
||||
table: Название таблицы
|
||||
data: Словарь с данными для вставки
|
||||
|
||||
Returns:
|
||||
Dict: Вставленная запись
|
||||
"""
|
||||
try:
|
||||
response = self.client.table(table).insert(data).execute()
|
||||
print(f"✅ Запись успешно добавлена в таблицу {table}")
|
||||
return response.data[0] if response.data else {}
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при вставке данных: {e}")
|
||||
return {}
|
||||
|
||||
def update(
|
||||
self,
|
||||
table: str,
|
||||
data: Dict[str, Any],
|
||||
filters: Dict[str, Any]
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Обновляет записи в таблице
|
||||
|
||||
Args:
|
||||
table: Название таблицы
|
||||
data: Словарь с данными для обновления
|
||||
filters: Словарь с фильтрами {column: value}
|
||||
|
||||
Returns:
|
||||
List[Dict]: Обновленные записи
|
||||
"""
|
||||
try:
|
||||
query = self.client.table(table).update(data)
|
||||
|
||||
for column, value in filters.items():
|
||||
query = query.eq(column, value)
|
||||
|
||||
response = query.execute()
|
||||
print(f"✅ Записи успешно обновлены в таблице {table}")
|
||||
return response.data
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при обновлении данных: {e}")
|
||||
return []
|
||||
|
||||
def delete(self, table: str, filters: Dict[str, Any]) -> List[Dict]:
|
||||
"""
|
||||
Удаляет записи из таблицы
|
||||
|
||||
Args:
|
||||
table: Название таблицы
|
||||
filters: Словарь с фильтрами {column: value}
|
||||
|
||||
Returns:
|
||||
List[Dict]: Удаленные записи
|
||||
"""
|
||||
try:
|
||||
query = self.client.table(table).delete()
|
||||
|
||||
for column, value in filters.items():
|
||||
query = query.eq(column, value)
|
||||
|
||||
response = query.execute()
|
||||
print(f"✅ Записи успешно удалены из таблицы {table}")
|
||||
return response.data
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при удалении данных: {e}")
|
||||
return []
|
||||
|
||||
# === Методы для работы с Storage ===
|
||||
|
||||
def upload_file(
|
||||
self,
|
||||
bucket: str,
|
||||
file_path: str,
|
||||
file_data: bytes,
|
||||
content_type: Optional[str] = None
|
||||
) -> bool:
|
||||
"""
|
||||
Загружает файл в Storage
|
||||
|
||||
Args:
|
||||
bucket: Название bucket
|
||||
file_path: Путь к файлу в bucket
|
||||
file_data: Данные файла в байтах
|
||||
content_type: MIME тип файла
|
||||
|
||||
Returns:
|
||||
bool: True если загрузка успешна
|
||||
"""
|
||||
try:
|
||||
options = {"content-type": content_type} if content_type else {}
|
||||
self.client.storage.from_(bucket).upload(
|
||||
file_path,
|
||||
file_data,
|
||||
file_options=options
|
||||
)
|
||||
print(f"✅ Файл {file_path} успешно загружен в bucket {bucket}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при загрузке файла: {e}")
|
||||
return False
|
||||
|
||||
def get_public_url(self, bucket: str, file_path: str) -> str:
|
||||
"""
|
||||
Получает публичный URL файла
|
||||
|
||||
Args:
|
||||
bucket: Название bucket
|
||||
file_path: Путь к файлу в bucket
|
||||
|
||||
Returns:
|
||||
str: Публичный URL
|
||||
"""
|
||||
try:
|
||||
url = self.client.storage.from_(bucket).get_public_url(file_path)
|
||||
return url
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при получении URL: {e}")
|
||||
return ""
|
||||
Reference in New Issue
Block a user