postgresdb3 0.3.5__tar.gz → 0.5.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.5.0/PKG-INFO +255 -0
  2. postgresdb3-0.5.0/README.md +233 -0
  3. postgresdb3-0.5.0/postgresdb3/__init__.py +1 -0
  4. {postgresdb3-0.3.5/postgresdb3 → postgresdb3-0.5.0/postgresdb3/core}/async_db.py +177 -9
  5. {postgresdb3-0.3.5/postgresdb3 → postgresdb3-0.5.0/postgresdb3/core}/sync_db.py +167 -10
  6. postgresdb3-0.5.0/postgresdb3/orm/__init__.py +1 -0
  7. postgresdb3-0.5.0/postgresdb3/orm/base.py +91 -0
  8. postgresdb3-0.5.0/postgresdb3/orm/fields/__init__.py +6 -0
  9. postgresdb3-0.5.0/postgresdb3/orm/fields/base.py +37 -0
  10. postgresdb3-0.5.0/postgresdb3/orm/fields/datetime.py +17 -0
  11. postgresdb3-0.5.0/postgresdb3/orm/fields/misc.py +28 -0
  12. postgresdb3-0.5.0/postgresdb3/orm/fields/numeric.py +32 -0
  13. postgresdb3-0.5.0/postgresdb3/orm/fields/relations.py +16 -0
  14. postgresdb3-0.5.0/postgresdb3/orm/fields/text.py +15 -0
  15. postgresdb3-0.5.0/postgresdb3/orm/meta.py +41 -0
  16. postgresdb3-0.5.0/postgresdb3/orm/models.py +276 -0
  17. postgresdb3-0.5.0/postgresdb3/orm/query.py +119 -0
  18. postgresdb3-0.5.0/postgresdb3.egg-info/PKG-INFO +255 -0
  19. postgresdb3-0.5.0/postgresdb3.egg-info/SOURCES.txt +24 -0
  20. {postgresdb3-0.3.5 → postgresdb3-0.5.0}/setup.py +1 -1
  21. postgresdb3-0.3.5/PKG-INFO +0 -209
  22. postgresdb3-0.3.5/README.md +0 -187
  23. postgresdb3-0.3.5/postgresdb3.egg-info/PKG-INFO +0 -209
  24. postgresdb3-0.3.5/postgresdb3.egg-info/SOURCES.txt +0 -11
  25. {postgresdb3-0.3.5/postgresdb3 → postgresdb3-0.5.0/postgresdb3/core}/__init__.py +0 -0
  26. {postgresdb3-0.3.5 → postgresdb3-0.5.0}/postgresdb3.egg-info/dependency_links.txt +0 -0
  27. {postgresdb3-0.3.5 → postgresdb3-0.5.0}/postgresdb3.egg-info/requires.txt +0 -0
  28. {postgresdb3-0.3.5 → postgresdb3-0.5.0}/postgresdb3.egg-info/top_level.txt +0 -0
  29. {postgresdb3-0.3.5 → postgresdb3-0.5.0}/pyproject.toml +0 -0
  30. {postgresdb3-0.3.5 → postgresdb3-0.5.0}/setup.cfg +0 -0
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: postgresdb3
3
+ Version: 0.5.0
4
+ Summary: Python uchun oddiy PostgreSQL wrapper
5
+ Author: Abdulbosit Alijonov
6
+ Project-URL: Source Code, https://github.com/AlijonovUz/PostgresDB
7
+ Classifier: Programming Language :: Python :: 3.9
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: psycopg2>=2.9
13
+ Requires-Dist: asyncpg>=0.31.0
14
+ Dynamic: author
15
+ Dynamic: classifier
16
+ Dynamic: description
17
+ Dynamic: description-content-type
18
+ Dynamic: project-url
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+ Dynamic: summary
22
+
23
+ # PostgresDB3
24
+
25
+ ![PyPI Version](https://img.shields.io/pypi/v/postgresdb3)
26
+ ![Python Version](https://img.shields.io/pypi/pyversions/postgresdb3)
27
+ ![License](https://img.shields.io/badge/license-MIT-green)
28
+
29
+ **PostgresDB3** — PostgreSQL bilan ishlash uchun oddiy, qulay va yengil Python wrapper.
30
+ Kutubxona sync va async ishlashni qo‘llab-quvvatlaydi hamda sodda ORM imkoniyatlarini ham taqdim etadi.
31
+
32
+ ---
33
+
34
+ # O‘rnatish
35
+
36
+ ```bash
37
+ pip install postgresdb3
38
+ ```
39
+
40
+ ---
41
+
42
+ # Asosiy imkoniyatlar
43
+
44
+ * PostgreSQL bilan **sync va async** ishlash
45
+ * CRUD amallari
46
+ * `where`, `join`, `group_by`, `order_by`, `limit`, `offset`
47
+ * `like`, `ilike`, `in`, `isnull`, `gt`, `lt` kabi filterlar
48
+ * Raw SQL bajarish imkoniyati
49
+ * Sodda ORM tizimi
50
+ * Async ORM qo‘llab-quvvatlash
51
+
52
+ ---
53
+
54
+ # Sync foydalanish
55
+
56
+ ```python
57
+ from postgresdb3 import PostgresDB
58
+
59
+ db = PostgresDB(
60
+ database="mydb",
61
+ user="postgres",
62
+ password="mypassword",
63
+ host="localhost",
64
+ port=5432
65
+ )
66
+
67
+ # Jadval yaratish
68
+ db.create("users", "id SERIAL PRIMARY KEY, name VARCHAR(100), age INT")
69
+
70
+ # Bitta qator qo‘shish
71
+ db.insert("users", "name, age", ("Ali", 25))
72
+
73
+ # Bir nechta qator qo‘shish
74
+ db.insert_many(
75
+ "users",
76
+ "name, age",
77
+ [("Vali", 30), ("Gulnoza", 22), ("Hasan", 28)]
78
+ )
79
+
80
+ # Barcha ma'lumotlarni olish
81
+ users = db.select("users")
82
+ print(users)
83
+
84
+ # Faqat kerakli ustunlarni olish
85
+ users = db.select("users", columns=["id", "name"])
86
+ print(users)
87
+
88
+ # Bitta qatorni olish
89
+ user = db.select("users", where={"name": "Ali"}, fetchone=True)
90
+ print(user)
91
+
92
+ # Shart bilan qidirish
93
+ adults = db.select("users", where={"age__gt": 18})
94
+ print(adults)
95
+
96
+ # LIKE qidiruv
97
+ users_like = db.select("users", where={"name__like": "%Ali%"})
98
+ print(users_like)
99
+
100
+ # ILIKE qidiruv
101
+ users_ilike = db.select("users", where={"name__ilike": "%ali%"})
102
+ print(users_ilike)
103
+
104
+ # IN operatori
105
+ selected_users = db.select("users", where={"id__in": [1, 2, 3]})
106
+ print(selected_users)
107
+
108
+ # NULL tekshirish
109
+ users_with_name = db.select("users", where={"name__isnull": False})
110
+ print(users_with_name)
111
+
112
+ # Tartiblash va limit
113
+ ordered_users = db.select(
114
+ "users",
115
+ where={"age__gt": 18},
116
+ order_by="age DESC",
117
+ limit=2
118
+ )
119
+ print(ordered_users)
120
+
121
+ # Pagination
122
+ paged_users = db.select(
123
+ "users",
124
+ order_by="id ASC",
125
+ limit=2,
126
+ offset=2
127
+ )
128
+ print(paged_users)
129
+
130
+ # Ma'lumotni yangilash
131
+ db.update("users", "age", 26, "name", "Ali")
132
+
133
+ # Ma'lumotni o‘chirish
134
+ db.delete("users", "name", "Ali")
135
+
136
+ # Jadvalni o‘chirish
137
+ db.drop("users", cascade=False)
138
+
139
+ # Raw SQL bajarish
140
+ result = db.raw("SELECT name, age FROM users WHERE age > %s", [25], fetchall=True)
141
+ print(result)
142
+
143
+ # Connectionni yopish
144
+ db.close()
145
+ ```
146
+
147
+ ---
148
+
149
+ # Async foydalanish
150
+
151
+ ```python
152
+ import asyncio
153
+ from postgresdb3 import AsyncPostgresDB
154
+
155
+ async def main():
156
+ db = AsyncPostgresDB(
157
+ database="mydb",
158
+ user="postgres",
159
+ password="mypassword",
160
+ host="localhost",
161
+ port=5432
162
+ )
163
+
164
+ await db.create("users", "id SERIAL PRIMARY KEY, name VARCHAR(100), age INT")
165
+
166
+ await db.insert("users", "name, age", ["Ali", 25])
167
+
168
+ await db.insert_many(
169
+ "users",
170
+ "name, age",
171
+ [["Vali", 30], ["Gulnoza", 22], ["Hasan", 28]]
172
+ )
173
+
174
+ users = await db.select("users")
175
+ print(users)
176
+
177
+ user = await db.select("users", where={"name": "Ali"}, fetchone=True)
178
+ print(user)
179
+
180
+ adults = await db.select("users", where={"age__gt": 18})
181
+ print(adults)
182
+
183
+ users_ilike = await db.select("users", where={"name__ilike": "%ali%"})
184
+ print(users_ilike)
185
+
186
+ await db.update("users", "age", 26, "name", "Ali")
187
+
188
+ await db.delete("users", "name", "Ali")
189
+
190
+ await db.drop("users", cascade=False)
191
+
192
+ result = await db.raw(
193
+ "SELECT name, age FROM users WHERE age > $1",
194
+ [25],
195
+ fetchall=True
196
+ )
197
+
198
+ print(result)
199
+
200
+ await db.close_pool()
201
+
202
+ asyncio.run(main())
203
+ ```
204
+
205
+ ---
206
+
207
+ # ORM foydalanish
208
+
209
+ ## Sync ORM
210
+
211
+ ```python
212
+ from postgresdb3 import PostgresDB
213
+ from postgresdb3.orm import Model
214
+ from postgresdb3.orm.fields import Serial, String, Integer, Text
215
+
216
+ db = PostgresDB(
217
+ database="mydb",
218
+ user="postgres",
219
+ password="mypassword"
220
+ )
221
+
222
+ class User(Model):
223
+ db = db
224
+ table = "users"
225
+
226
+ id = Serial(primary_key=True)
227
+ name = String()
228
+ age = Integer()
229
+ about = Text(nullable=True)
230
+
231
+ User.create_table()
232
+
233
+ user = User.create(name="Ali", age=20)
234
+
235
+ print(User.all())
236
+
237
+ user = User.find(1)
238
+
239
+ user.update(name="Valijon")
240
+
241
+ user.delete()
242
+ ```
243
+
244
+ ---
245
+
246
+ # Qo‘shimcha
247
+
248
+ `%s` va `$1` bilan parametrizatsiya qilish xavfsiz va SQL injection'dan himoya qiladi.
249
+
250
+ `where` va `join` yordamida murakkab so‘rovlar yozish mumkin.
251
+
252
+ `limit`, `offset` va `order_by` parametrlaridan foydalanib ma'lumotlarni tartiblash va sahifalash oson.
253
+
254
+ `raw()` metodi orqali kerak bo‘lganda to‘g‘ridan-to‘g‘ri SQL so‘rov yozish mumkin.
255
+
@@ -0,0 +1,233 @@
1
+ # PostgresDB3
2
+
3
+ ![PyPI Version](https://img.shields.io/pypi/v/postgresdb3)
4
+ ![Python Version](https://img.shields.io/pypi/pyversions/postgresdb3)
5
+ ![License](https://img.shields.io/badge/license-MIT-green)
6
+
7
+ **PostgresDB3** — PostgreSQL bilan ishlash uchun oddiy, qulay va yengil Python wrapper.
8
+ Kutubxona sync va async ishlashni qo‘llab-quvvatlaydi hamda sodda ORM imkoniyatlarini ham taqdim etadi.
9
+
10
+ ---
11
+
12
+ # O‘rnatish
13
+
14
+ ```bash
15
+ pip install postgresdb3
16
+ ```
17
+
18
+ ---
19
+
20
+ # Asosiy imkoniyatlar
21
+
22
+ * PostgreSQL bilan **sync va async** ishlash
23
+ * CRUD amallari
24
+ * `where`, `join`, `group_by`, `order_by`, `limit`, `offset`
25
+ * `like`, `ilike`, `in`, `isnull`, `gt`, `lt` kabi filterlar
26
+ * Raw SQL bajarish imkoniyati
27
+ * Sodda ORM tizimi
28
+ * Async ORM qo‘llab-quvvatlash
29
+
30
+ ---
31
+
32
+ # Sync foydalanish
33
+
34
+ ```python
35
+ from postgresdb3 import PostgresDB
36
+
37
+ db = PostgresDB(
38
+ database="mydb",
39
+ user="postgres",
40
+ password="mypassword",
41
+ host="localhost",
42
+ port=5432
43
+ )
44
+
45
+ # Jadval yaratish
46
+ db.create("users", "id SERIAL PRIMARY KEY, name VARCHAR(100), age INT")
47
+
48
+ # Bitta qator qo‘shish
49
+ db.insert("users", "name, age", ("Ali", 25))
50
+
51
+ # Bir nechta qator qo‘shish
52
+ db.insert_many(
53
+ "users",
54
+ "name, age",
55
+ [("Vali", 30), ("Gulnoza", 22), ("Hasan", 28)]
56
+ )
57
+
58
+ # Barcha ma'lumotlarni olish
59
+ users = db.select("users")
60
+ print(users)
61
+
62
+ # Faqat kerakli ustunlarni olish
63
+ users = db.select("users", columns=["id", "name"])
64
+ print(users)
65
+
66
+ # Bitta qatorni olish
67
+ user = db.select("users", where={"name": "Ali"}, fetchone=True)
68
+ print(user)
69
+
70
+ # Shart bilan qidirish
71
+ adults = db.select("users", where={"age__gt": 18})
72
+ print(adults)
73
+
74
+ # LIKE qidiruv
75
+ users_like = db.select("users", where={"name__like": "%Ali%"})
76
+ print(users_like)
77
+
78
+ # ILIKE qidiruv
79
+ users_ilike = db.select("users", where={"name__ilike": "%ali%"})
80
+ print(users_ilike)
81
+
82
+ # IN operatori
83
+ selected_users = db.select("users", where={"id__in": [1, 2, 3]})
84
+ print(selected_users)
85
+
86
+ # NULL tekshirish
87
+ users_with_name = db.select("users", where={"name__isnull": False})
88
+ print(users_with_name)
89
+
90
+ # Tartiblash va limit
91
+ ordered_users = db.select(
92
+ "users",
93
+ where={"age__gt": 18},
94
+ order_by="age DESC",
95
+ limit=2
96
+ )
97
+ print(ordered_users)
98
+
99
+ # Pagination
100
+ paged_users = db.select(
101
+ "users",
102
+ order_by="id ASC",
103
+ limit=2,
104
+ offset=2
105
+ )
106
+ print(paged_users)
107
+
108
+ # Ma'lumotni yangilash
109
+ db.update("users", "age", 26, "name", "Ali")
110
+
111
+ # Ma'lumotni o‘chirish
112
+ db.delete("users", "name", "Ali")
113
+
114
+ # Jadvalni o‘chirish
115
+ db.drop("users", cascade=False)
116
+
117
+ # Raw SQL bajarish
118
+ result = db.raw("SELECT name, age FROM users WHERE age > %s", [25], fetchall=True)
119
+ print(result)
120
+
121
+ # Connectionni yopish
122
+ db.close()
123
+ ```
124
+
125
+ ---
126
+
127
+ # Async foydalanish
128
+
129
+ ```python
130
+ import asyncio
131
+ from postgresdb3 import AsyncPostgresDB
132
+
133
+ async def main():
134
+ db = AsyncPostgresDB(
135
+ database="mydb",
136
+ user="postgres",
137
+ password="mypassword",
138
+ host="localhost",
139
+ port=5432
140
+ )
141
+
142
+ await db.create("users", "id SERIAL PRIMARY KEY, name VARCHAR(100), age INT")
143
+
144
+ await db.insert("users", "name, age", ["Ali", 25])
145
+
146
+ await db.insert_many(
147
+ "users",
148
+ "name, age",
149
+ [["Vali", 30], ["Gulnoza", 22], ["Hasan", 28]]
150
+ )
151
+
152
+ users = await db.select("users")
153
+ print(users)
154
+
155
+ user = await db.select("users", where={"name": "Ali"}, fetchone=True)
156
+ print(user)
157
+
158
+ adults = await db.select("users", where={"age__gt": 18})
159
+ print(adults)
160
+
161
+ users_ilike = await db.select("users", where={"name__ilike": "%ali%"})
162
+ print(users_ilike)
163
+
164
+ await db.update("users", "age", 26, "name", "Ali")
165
+
166
+ await db.delete("users", "name", "Ali")
167
+
168
+ await db.drop("users", cascade=False)
169
+
170
+ result = await db.raw(
171
+ "SELECT name, age FROM users WHERE age > $1",
172
+ [25],
173
+ fetchall=True
174
+ )
175
+
176
+ print(result)
177
+
178
+ await db.close_pool()
179
+
180
+ asyncio.run(main())
181
+ ```
182
+
183
+ ---
184
+
185
+ # ORM foydalanish
186
+
187
+ ## Sync ORM
188
+
189
+ ```python
190
+ from postgresdb3 import PostgresDB
191
+ from postgresdb3.orm import Model
192
+ from postgresdb3.orm.fields import Serial, String, Integer, Text
193
+
194
+ db = PostgresDB(
195
+ database="mydb",
196
+ user="postgres",
197
+ password="mypassword"
198
+ )
199
+
200
+ class User(Model):
201
+ db = db
202
+ table = "users"
203
+
204
+ id = Serial(primary_key=True)
205
+ name = String()
206
+ age = Integer()
207
+ about = Text(nullable=True)
208
+
209
+ User.create_table()
210
+
211
+ user = User.create(name="Ali", age=20)
212
+
213
+ print(User.all())
214
+
215
+ user = User.find(1)
216
+
217
+ user.update(name="Valijon")
218
+
219
+ user.delete()
220
+ ```
221
+
222
+ ---
223
+
224
+ # Qo‘shimcha
225
+
226
+ `%s` va `$1` bilan parametrizatsiya qilish xavfsiz va SQL injection'dan himoya qiladi.
227
+
228
+ `where` va `join` yordamida murakkab so‘rovlar yozish mumkin.
229
+
230
+ `limit`, `offset` va `order_by` parametrlaridan foydalanib ma'lumotlarni tartiblash va sahifalash oson.
231
+
232
+ `raw()` metodi orqali kerak bo‘lganda to‘g‘ridan-to‘g‘ri SQL so‘rov yozish mumkin.
233
+
@@ -0,0 +1 @@
1
+ from .core import PostgresDB, AsyncPostgresDB
@@ -95,11 +95,171 @@ class AsyncPostgresDB:
95
95
 
96
96
  await self._manager(sql, commit=True)
97
97
 
98
+ 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
+ if not isinstance(value, str) or not value.strip():
104
+ raise ValueError(f"{name} bo'sh bo'lmasligi kerak")
105
+
106
+ cleaned = value.replace("_", "").replace(".", "")
107
+ if not cleaned.isalnum():
108
+ raise ValueError(f"Invalid {name}: {value}")
109
+
110
+ return value
111
+
112
+ 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
+ if isinstance(columns, list):
119
+ if not columns:
120
+ raise ValueError("columns bo'sh list bo'lmasligi kerak")
121
+
122
+ validated_columns = [self._validate_identifier(col, "column") for col in columns]
123
+ return ", ".join(validated_columns)
124
+
125
+ if not isinstance(columns, str) or not columns.strip():
126
+ raise ValueError("columns noto'g'ri bo'lishi mumkin emas")
127
+
128
+ return columns
129
+
130
+ 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
+ if where is None:
154
+ return "", []
155
+
156
+ if isinstance(where, tuple) and len(where) == 2:
157
+ condition, values = where
158
+ return condition, list(values)
159
+
160
+ if isinstance(where, dict):
161
+ clauses = []
162
+ params = []
163
+ index = start_index
164
+
165
+ operator_map = {
166
+ "eq": "=",
167
+ "ne": "!=",
168
+ "gt": ">",
169
+ "gte": ">=",
170
+ "lt": "<",
171
+ "lte": "<=",
172
+ "like": "LIKE",
173
+ "ilike": "ILIKE",
174
+ }
175
+
176
+ for key, value in where.items():
177
+ if "__" in key:
178
+ field, op = key.rsplit("__", 1)
179
+ else:
180
+ field, op = key, "eq"
181
+
182
+ self._validate_identifier(field, "where field")
183
+
184
+ if op in operator_map:
185
+ clauses.append(f"{field} {operator_map[op]} ${index}")
186
+ params.append(value)
187
+ index += 1
188
+
189
+ elif op == "in":
190
+ if not isinstance(value, (list, tuple)) or not value:
191
+ raise ValueError(f"{field}__in uchun bo'sh bo'lmagan list/tuple kerak")
192
+
193
+ placeholders = ", ".join(f"${i}" for i in range(index, index + len(value)))
194
+ clauses.append(f"{field} IN ({placeholders})")
195
+ params.extend(value)
196
+ index += len(value)
197
+
198
+ elif op == "not_in":
199
+ if not isinstance(value, (list, tuple)) or not value:
200
+ raise ValueError(f"{field}__not_in uchun bo'sh bo'lmagan list/tuple kerak")
201
+
202
+ placeholders = ", ".join(f"${i}" for i in range(index, index + len(value)))
203
+ clauses.append(f"{field} NOT IN ({placeholders})")
204
+ params.extend(value)
205
+ index += len(value)
206
+
207
+ elif op == "isnull":
208
+ if value:
209
+ clauses.append(f"{field} IS NULL")
210
+ else:
211
+ clauses.append(f"{field} IS NOT NULL")
212
+
213
+ else:
214
+ raise ValueError(f"Noma'lum operator: {op}")
215
+
216
+ return " AND ".join(clauses), params
217
+
218
+ if isinstance(where, list):
219
+ clauses = []
220
+ params = []
221
+ index = start_index
222
+
223
+ for item in where:
224
+ if len(item) != 3:
225
+ raise ValueError("List formatidagi where elementlari (field, operator, value) bo'lishi kerak")
226
+
227
+ field, operator, value = item
228
+ self._validate_identifier(field, "where field")
229
+ op = operator.upper()
230
+
231
+ if op in {"=", "!=", ">", ">=", "<", "<=", "LIKE", "ILIKE"}:
232
+ clauses.append(f"{field} {op} ${index}")
233
+ params.append(value)
234
+ index += 1
235
+
236
+ elif op in {"IN", "NOT IN"}:
237
+ if not isinstance(value, (list, tuple)) or not value:
238
+ raise ValueError(f"{field} {op} uchun bo'sh bo'lmagan list/tuple kerak")
239
+
240
+ placeholders = ", ".join(f"${i}" for i in range(index, index + len(value)))
241
+ clauses.append(f"{field} {op} ({placeholders})")
242
+ params.extend(value)
243
+ index += len(value)
244
+
245
+ elif op == "IS NULL":
246
+ clauses.append(f"{field} IS NULL")
247
+
248
+ elif op == "IS NOT NULL":
249
+ clauses.append(f"{field} IS NOT NULL")
250
+
251
+ else:
252
+ raise ValueError(f"Noma'lum operator: {operator}")
253
+
254
+ return " AND ".join(clauses), params
255
+
256
+ raise TypeError("where tuple, dict yoki list bo'lishi kerak")
257
+
98
258
  async def select(
99
259
  self,
100
260
  table: str,
101
- columns: str = "*",
102
- where: Optional[List[Any]] = None,
261
+ columns: str | list[str] = "*",
262
+ where: tuple | dict | list | None = None,
103
263
  join: Optional[List[tuple]] = None,
104
264
  group_by: Optional[str] = None,
105
265
  order_by: Optional[str] = None,
@@ -112,8 +272,11 @@ class AsyncPostgresDB:
112
272
 
113
273
  Args:
114
274
  table (str): Asosiy jadval nomi.
115
- columns (str): Tanlanadigan ustunlar (default: "*").
116
- where (Optional[List[Any]]): ("shart", [qiymatlar])
275
+ columns (str | list[str]): Tanlanadigan ustunlar.
276
+ where:
277
+ tuple — ("age > $1", [18])
278
+ dict — {"age__gt": 18, "name__ilike": "%ali%"}
279
+ list — [("age", ">", 18), ("name", "ILIKE", "%ali%")]
117
280
  join (Optional[List[tuple]]): [("INNER JOIN", "orders", "users.id = orders.user_id")]
118
281
  group_by (Optional[str]): GROUP BY ustuni.
119
282
  order_by (Optional[str]): ORDER BY qoidasi.
@@ -124,17 +287,22 @@ class AsyncPostgresDB:
124
287
  Returns:
125
288
  asyncpg.Record yoki list[asyncpg.Record]
126
289
  """
290
+ table = self._validate_identifier(table, "table")
291
+ columns = self._normalize_columns(columns)
292
+
127
293
  sql = f"SELECT {columns} FROM {table}"
128
294
  params: List[Any] = []
129
295
 
130
296
  if join:
131
297
  for join_type, join_table, on_condition in join:
298
+ join_table = self._validate_identifier(join_table, "join table")
132
299
  sql += f" {join_type} {join_table} ON {on_condition}"
133
300
 
134
301
  if where:
135
- condition, values = where
136
- sql += f" WHERE {condition}"
137
- params.extend(values)
302
+ condition, values = self._build_where(where, start_index=1)
303
+ if condition:
304
+ sql += f" WHERE {condition}"
305
+ params.extend(values)
138
306
 
139
307
  if group_by:
140
308
  sql += f" GROUP BY {group_by}"
@@ -143,11 +311,11 @@ class AsyncPostgresDB:
143
311
  sql += f" ORDER BY {order_by}"
144
312
 
145
313
  if limit is not None:
146
- sql += f" LIMIT $%d" % (len(params) + 1)
314
+ sql += f" LIMIT ${len(params) + 1}"
147
315
  params.append(limit)
148
316
 
149
317
  if offset is not None:
150
- sql += " OFFSET $%d" % (len(params) + 1)
318
+ sql += f" OFFSET ${len(params) + 1}"
151
319
  params.append(offset)
152
320
 
153
321
  return await self._manager(sql, *params, fetchone=fetchone, fetchall=not fetchone)