postgresdb3 0.6.0__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/PKG-INFO +1 -1
  2. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/core/async_db.py +116 -179
  3. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/core/sync_db.py +114 -227
  4. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/fields/__init__.py +1 -1
  5. postgresdb3-0.7.0/postgresdb3/orm/fields/foreign.py +31 -0
  6. postgresdb3-0.7.0/postgresdb3/orm/meta.py +61 -0
  7. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/models.py +45 -19
  8. postgresdb3-0.7.0/postgresdb3/orm/query.py +473 -0
  9. postgresdb3-0.7.0/postgresdb3/orm/relations.py +44 -0
  10. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3.egg-info/PKG-INFO +1 -1
  11. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3.egg-info/SOURCES.txt +2 -1
  12. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/setup.py +1 -1
  13. postgresdb3-0.6.0/postgresdb3/orm/fields/relations.py +0 -16
  14. postgresdb3-0.6.0/postgresdb3/orm/meta.py +0 -41
  15. postgresdb3-0.6.0/postgresdb3/orm/query.py +0 -283
  16. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/README.md +0 -0
  17. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/__init__.py +0 -0
  18. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/core/__init__.py +0 -0
  19. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/__init__.py +0 -0
  20. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/base.py +0 -0
  21. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/fields/base.py +0 -0
  22. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/fields/datetime.py +0 -0
  23. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/fields/misc.py +0 -0
  24. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/fields/numeric.py +0 -0
  25. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3/orm/fields/text.py +0 -0
  26. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3.egg-info/dependency_links.txt +0 -0
  27. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3.egg-info/requires.txt +0 -0
  28. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/postgresdb3.egg-info/top_level.txt +0 -0
  29. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/pyproject.toml +0 -0
  30. {postgresdb3-0.6.0 → postgresdb3-0.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: postgresdb3
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Python uchun oddiy PostgreSQL wrapper
5
5
  Author: Abdulbosit Alijonov
6
6
  Project-URL: Source Code, https://github.com/AlijonovUz/PostgresDB
@@ -3,25 +3,7 @@ from typing import Any, List, Optional
3
3
 
4
4
 
5
5
  class AsyncPostgresDB:
6
- """
7
- Async PostgreSQL helper class for CRUD operations and schema management.
8
-
9
- Ushbu class `asyncpg` kutubxonasidan foydalanib,
10
- PostgreSQL bilan asynchronous CRUD va schema operatsiyalarini bajaradi.
11
- Aiogram kabi async frameworklar bilan to‘g‘ridan-to‘g‘ri ishlash uchun mos.
12
- """
13
-
14
6
  def __init__(self, database: str, user: str, password: str, host: str = "localhost", port: int = 5432) -> None:
15
- """
16
- PostgreSQL bazasiga ulanishni tayyorlaydi.
17
-
18
- Args:
19
- database (str): Bazaning nomi.
20
- user (str): Foydalanuvchi nomi.
21
- password (str): Parol.
22
- host (str): Server manzili (default: "localhost").
23
- port (int): Port (default: 5432).
24
- """
25
7
  self.database = database
26
8
  self.user = user
27
9
  self.password = password
@@ -30,21 +12,6 @@ class AsyncPostgresDB:
30
12
  self.pool: Optional[asyncpg.pool.Pool] = None
31
13
 
32
14
  async def _manager(self, sql: str, *params, fetchone=False, fetchall=False, commit=False) -> Any:
33
- """
34
- Markaziy metod: barcha SQL so‘rovlarni bajaradi.
35
-
36
- Args:
37
- sql (str): Bajariladigan SQL so‘rov.
38
- *params: SQL parametrlarini berish.
39
- fetchone (bool): True bo‘lsa, faqat bitta qator qaytaradi.
40
- fetchall (bool): True bo‘lsa, barcha natijalarni qaytaradi.
41
- commit (bool): True bo‘lsa, tranzaksiya commit qilinadi.
42
-
43
- Returns:
44
- fetchone=True bo‘lsa: asyncpg.Record
45
- fetchall=True bo‘lsa: list[asyncpg.Record]
46
- commit=True bo‘lsa: None
47
- """
48
15
  if not self.pool:
49
16
  self.pool = await asyncpg.create_pool(
50
17
  database=self.database,
@@ -56,39 +23,21 @@ class AsyncPostgresDB:
56
23
 
57
24
  async with self.pool.acquire() as conn:
58
25
  if commit:
59
- await conn.execute(sql, *params)
60
- return
26
+ return await conn.execute(sql, *params)
61
27
  if fetchone:
62
28
  return await conn.fetchrow(sql, *params)
63
29
  if fetchall:
64
30
  return await conn.fetch(sql, *params)
65
31
 
66
32
  async def close_pool(self) -> None:
67
- """
68
- Connection poolni yopadi.
69
- """
70
33
  if self.pool:
71
34
  await self.pool.close()
72
35
  self.pool = None
73
36
 
74
37
  async def create(self, table: str, columns: str) -> None:
75
- """
76
- Jadval yaratadi (agar mavjud bo‘lsa o‘tkazib yuboradi).
77
-
78
- Args:
79
- table (str): Jadval nomi.
80
- columns (str): Ustunlar va turlari, misol: "id SERIAL PRIMARY KEY, name TEXT".
81
- """
82
38
  await self._manager(f"CREATE TABLE IF NOT EXISTS {table} ({columns})", commit=True)
83
39
 
84
40
  async def drop(self, table: str, cascade: bool = False) -> None:
85
- """
86
- Jadvalni o‘chiradi (agar mavjud bo‘lsa).
87
-
88
- Args:
89
- table (str): Jadval nomi.
90
- cascade (bool): Agar True bo‘lsa, jadval bilan bog‘liq barcha obyektlar ham o‘chiradi. Default: True
91
- """
92
41
  sql = f"DROP TABLE IF EXISTS {table}"
93
42
  if cascade:
94
43
  sql += " CASCADE"
@@ -96,10 +45,6 @@ class AsyncPostgresDB:
96
45
  await self._manager(sql, commit=True)
97
46
 
98
47
  def _validate_identifier(self, value: str, name: str = "identifier") -> str:
99
- """
100
- Jadval yoki ustun nomi uchun minimal tekshiruv.
101
- Faqat harf, raqam, underscore va nuqtaga ruxsat beradi.
102
- """
103
48
  if not isinstance(value, str) or not value.strip():
104
49
  raise ValueError(f"{name} bo'sh bo'lmasligi kerak")
105
50
 
@@ -110,11 +55,6 @@ class AsyncPostgresDB:
110
55
  return value
111
56
 
112
57
  def _normalize_columns(self, columns: str | list[str]) -> str:
113
- """
114
- columns list bo'lsa stringga aylantiradi.
115
- list bo'lsa har bir column validate qilinadi.
116
- str bo'lsa SQL expression bo'lishi mumkinligi uchun o'z holicha qaytariladi.
117
- """
118
58
  if isinstance(columns, list):
119
59
  if not columns:
120
60
  raise ValueError("columns bo'sh list bo'lmasligi kerak")
@@ -128,28 +68,6 @@ class AsyncPostgresDB:
128
68
  return columns
129
69
 
130
70
  def _build_where(self, where: Any, start_index: int = 1) -> tuple[str, list]:
131
- """
132
- Qo'llab-quvvatlanadi:
133
-
134
- 1) tuple:
135
- ("age > $1", [18])
136
-
137
- 2) dict:
138
- {
139
- "age__gt": 18,
140
- "name__ilike": "%ali%",
141
- "status": "active",
142
- "id__in": [1, 2, 3],
143
- "deleted_at__isnull": True
144
- }
145
-
146
- 3) list[tuple]:
147
- [
148
- ("age", ">", 18),
149
- ("name", "ILIKE", "%ali%"),
150
- ("status", "=", "active")
151
- ]
152
- """
153
71
  if where is None:
154
72
  return "", []
155
73
 
@@ -268,26 +186,6 @@ class AsyncPostgresDB:
268
186
  offset: Optional[int] = None,
269
187
  fetchone: bool = False
270
188
  ) -> Any:
271
- """
272
- Jadvaldan ma'lumotlarni tanlash.
273
-
274
- Args:
275
- table (str): Asosiy jadval nomi.
276
- columns (str | list[str]): Tanlanadigan ustunlar.
277
- where:
278
- tuple — ("age > $1", [18])
279
- dict — {"age__gt": 18, "name__ilike": "%ali%"}
280
- list — [("age", ">", 18), ("name", "ILIKE", "%ali%")]
281
- join (Optional[List[tuple]]): [("INNER JOIN", "orders", "users.id = orders.user_id")]
282
- group_by (Optional[str]): GROUP BY ustuni.
283
- order_by (Optional[str]): ORDER BY qoidasi.
284
- limit (Optional[int]): LIMIT qiymati.
285
- offset (Optional[int]): OFFSET qiymati.
286
- fetchone (bool): True bo‘lsa faqat bitta qator qaytaradi.
287
-
288
- Returns:
289
- asyncpg.Record yoki list[asyncpg.Record]
290
- """
291
189
  table = self._validate_identifier(table, "table")
292
190
  columns = self._normalize_columns(columns)
293
191
 
@@ -321,44 +219,17 @@ class AsyncPostgresDB:
321
219
 
322
220
  return await self._manager(sql, *params, fetchone=fetchone, fetchall=not fetchone)
323
221
 
324
- async def insert(self, table: str, columns: str, values: List[Any]) -> None:
325
- """
326
- Jadvalga yangi qator qo‘shadi.
327
-
328
- Args:
329
- table (str): Jadval nomi.
330
- columns (str): Ustunlar nomi, misol: "name, age".
331
- values (List[Any]): Qiymatlar, misol: ["Ali", 25].
332
- """
222
+ async def insert(self, table: str, columns: str, values: list, returning: str | None = None):
333
223
  placeholders = ", ".join(f"${i + 1}" for i in range(len(values)))
334
224
  sql = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
225
+
226
+ if returning:
227
+ sql += f" RETURNING {returning}"
228
+ return await self._manager(sql, *values, fetchone=True)
229
+
335
230
  await self._manager(sql, *values, commit=True)
336
231
 
337
232
  async def insert_many(self, table: str, columns: str, values_list: List[List[Any]]) -> None:
338
- """
339
- Jadvalga bir nechta qator qo‘shish (bulk insert).
340
-
341
- Args:
342
- table (str): Jadval nomi.
343
- columns (str): Ustunlar nomi, misol: "name, age".
344
- values_list (List[List[Any]]): Qiymatlar ro‘yxati, misol: [["Ali", 25], ["Vali", 30]].
345
-
346
- Raises:
347
- ValueError: Agar `values_list` bo‘sh bo‘lsa.
348
-
349
- Notes:
350
- - `asyncpg` kutubxonasida har bir parametr uchun unikal `$n` indekslari kerak,
351
- shuning uchun har bir qator uchun ketma-ket `$1, $2, ...` ishlatiladi.
352
- - Metod barcha qatorlarni bir so‘rovda qo‘shadi.
353
- - `fetchmany` kerak emas, chunki bu insert operatsiyasi natija qaytarmaydi.
354
-
355
- Example:
356
- await db.insert_many(
357
- "users",
358
- "name, age",
359
- [["Ali", 25], ["Vali", 30], ["Guli", 22]]
360
- )
361
- """
362
233
  if not values_list:
363
234
  raise ValueError("values_list bo'sh bo'lishi mumkin emas")
364
235
 
@@ -376,41 +247,124 @@ class AsyncPostgresDB:
376
247
  await self._manager(sql, *flat_values, commit=True)
377
248
 
378
249
  async def update(self, table: str, set_column: str, set_value: Any, where_column: str, where_value: Any) -> None:
379
- """
380
- Jadvaldagi qatorni yangilaydi.
381
-
382
- Args:
383
- table (str): Jadval nomi.
384
- set_column (str): O‘zgartiriladigan ustun.
385
- set_value (Any): Yangi qiymat.
386
- where_column (str): Filtrlash ustuni.
387
- where_value (Any): Filtrlash qiymati.
388
- """
389
250
  sql = f"UPDATE {table} SET {set_column} = $1 WHERE {where_column} = $2"
390
251
  await self._manager(sql, set_value, where_value, commit=True)
391
252
 
253
+ async def update_fields(self, table: str, data: dict, where_column: str, where_value: Any) -> int:
254
+ table = self._validate_identifier(table, "table")
255
+ where_column = self._validate_identifier(where_column, "where column")
256
+
257
+ if not data:
258
+ raise ValueError("Yangilash uchun data bo'sh bo'lmasligi kerak")
259
+
260
+ set_parts = []
261
+ params = []
262
+ index = 1
263
+
264
+ for key, value in data.items():
265
+ key = self._validate_identifier(key, "column")
266
+ set_parts.append(f"{key} = ${index}")
267
+ params.append(value)
268
+ index += 1
269
+
270
+ sql = f"UPDATE {table} SET {', '.join(set_parts)} WHERE {where_column} = ${index}"
271
+ params.append(where_value)
272
+
273
+ result = await self._manager(sql, *params, commit=True)
274
+ return int(result.split()[-1])
275
+
276
+ async def update_where(self, table: str, data: dict, where: tuple | dict | list) -> int:
277
+ table = self._validate_identifier(table, "table")
278
+
279
+ if not data:
280
+ raise ValueError("Yangilash uchun data bo'sh bo'lmasligi kerak")
281
+
282
+ if not where:
283
+ raise ValueError("update_where uchun where bo'sh bo'lmasligi kerak")
284
+
285
+ set_parts = []
286
+ params = []
287
+ index = 1
288
+
289
+ for key, value in data.items():
290
+ key = self._validate_identifier(key, "column")
291
+ set_parts.append(f"{key} = ${index}")
292
+ params.append(value)
293
+ index += 1
294
+
295
+ where_sql, where_params = self._build_where(where, start_index=index)
296
+
297
+ if not where_sql:
298
+ raise ValueError("WHERE sharti noto'g'ri")
299
+
300
+ sql = f"UPDATE {table} SET {', '.join(set_parts)} WHERE {where_sql}"
301
+
302
+ result = await self._manager(
303
+ sql,
304
+ *(params + where_params),
305
+ commit=True
306
+ )
307
+
308
+ return int(result.split()[-1])
309
+
392
310
  async def delete(self, table: str, where_column: str, where_value: Any) -> None:
393
- """
394
- Jadvaldan qator o‘chiradi.
395
-
396
- Args:
397
- table (str): Jadval nomi.
398
- where_column (str): Filtrlash ustuni.
399
- where_value (Any): Filtrlash qiymati.
400
- """
401
311
  sql = f"DELETE FROM {table} WHERE {where_column} = $1"
402
312
  await self._manager(sql, where_value, commit=True)
403
313
 
404
- async def list_tables(self, schema: str = "public") -> List[str]:
405
- """
406
- Bazadagi barcha jadvallarni ro‘yxat sifatida qaytaradi.
314
+ async def delete_where(self, table: str, where: tuple | dict | list) -> int:
315
+ table = self._validate_identifier(table, "table")
316
+
317
+ if not where:
318
+ raise ValueError("delete_where uchun where bo'sh bo'lmasligi kerak")
319
+
320
+ where_sql, where_params = self._build_where(where, start_index=1)
407
321
 
408
- Args:
409
- schema (str): Schema nomi, default "public".
322
+ if not where_sql:
323
+ raise ValueError("WHERE sharti noto'g'ri")
410
324
 
411
- Returns:
412
- List[str]: Jadval nomlari
413
- """
325
+ sql = f"DELETE FROM {table} WHERE {where_sql}"
326
+
327
+ result = await self._manager(
328
+ sql,
329
+ *where_params,
330
+ commit=True
331
+ )
332
+
333
+ return int(result.split()[-1])
334
+
335
+ async def exists_where(
336
+ self,
337
+ table: str,
338
+ where: tuple | dict | list | None = None,
339
+ join: list[tuple] | None = None,
340
+ group_by: str | None = None,
341
+ ) -> bool:
342
+ table = self._validate_identifier(table, "table")
343
+
344
+ sql = f"SELECT 1 FROM {table}"
345
+ params = []
346
+
347
+ if join:
348
+ for join_type, join_table, on_condition in join:
349
+ join_table = self._validate_identifier(join_table, "join table")
350
+ sql += f" {join_type} {join_table} ON {on_condition}"
351
+
352
+ if where:
353
+ condition, values = self._build_where(where, start_index=1)
354
+ if condition:
355
+ sql += f" WHERE {condition}"
356
+ params.extend(values)
357
+
358
+ if group_by:
359
+ sql += f" GROUP BY {group_by}"
360
+
361
+ sql += f" LIMIT ${len(params) + 1}"
362
+ params.append(1)
363
+
364
+ record = await self._manager(sql, *params, fetchone=True)
365
+ return record is not None
366
+
367
+ async def list_tables(self, schema: str = "public") -> List[str]:
414
368
  sql = """
415
369
  SELECT table_name
416
370
  FROM information_schema.tables
@@ -421,16 +375,6 @@ class AsyncPostgresDB:
421
375
  return [r["table_name"] for r in result]
422
376
 
423
377
  async def describe_table(self, table: str, schema: str = "public") -> List[str]:
424
- """
425
- Jadval ustunlari haqida ma'lumot beradi.
426
-
427
- Args:
428
- table (str): Jadval nomi.
429
- schema (str): Schema nomi, default "public".
430
-
431
- Returns:
432
- list[asyncpg.Record]: Har bir ustun bo‘yicha ma’lumot
433
- """
434
378
  sql = """
435
379
  SELECT column_name, data_type, is_nullable, column_default
436
380
  FROM information_schema.columns
@@ -441,12 +385,5 @@ class AsyncPostgresDB:
441
385
  return await self._manager(sql, schema, table, fetchall=True)
442
386
 
443
387
  async def alter(self, table: str, action: str) -> None:
444
- """
445
- Jadval strukturasi o‘zgartirish (ALTER TABLE).
446
-
447
- Args:
448
- table (str): Jadval nomi.
449
- action (str): ALTER TABLE dan keyingi SQL qismi, misol: "ADD COLUMN age INT".
450
- """
451
388
  sql = f"ALTER TABLE {table} {action}"
452
389
  await self._manager(sql, commit=True)