postgresdb3 1.0.4__tar.gz → 2.0.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 (35) hide show
  1. postgresdb3-2.0.0/PKG-INFO +223 -0
  2. postgresdb3-2.0.0/README.md +201 -0
  3. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/cli.py +16 -8
  4. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/core/async_db.py +47 -13
  5. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/core/sync_db.py +64 -6
  6. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/migrations/engine.py +105 -13
  7. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/base.py +17 -5
  8. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/fields/base.py +7 -0
  9. postgresdb3-2.0.0/postgresdb3/orm/fields/text.py +23 -0
  10. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/meta.py +16 -6
  11. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/models.py +90 -4
  12. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/query.py +299 -31
  13. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/relations.py +17 -28
  14. postgresdb3-2.0.0/postgresdb3.egg-info/PKG-INFO +223 -0
  15. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/setup.py +1 -1
  16. postgresdb3-1.0.4/PKG-INFO +0 -302
  17. postgresdb3-1.0.4/README.md +0 -280
  18. postgresdb3-1.0.4/postgresdb3/orm/fields/text.py +0 -15
  19. postgresdb3-1.0.4/postgresdb3.egg-info/PKG-INFO +0 -302
  20. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/__init__.py +0 -0
  21. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/core/__init__.py +0 -0
  22. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/migrations/__init__.py +0 -0
  23. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/__init__.py +0 -0
  24. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/expressions.py +0 -0
  25. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/fields/__init__.py +0 -0
  26. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/fields/datetime.py +0 -0
  27. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/fields/foreign.py +0 -0
  28. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/fields/misc.py +0 -0
  29. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3/orm/fields/numeric.py +0 -0
  30. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3.egg-info/SOURCES.txt +0 -0
  31. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3.egg-info/dependency_links.txt +0 -0
  32. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3.egg-info/requires.txt +0 -0
  33. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/postgresdb3.egg-info/top_level.txt +0 -0
  34. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/pyproject.toml +0 -0
  35. {postgresdb3-1.0.4 → postgresdb3-2.0.0}/setup.cfg +0 -0
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.4
2
+ Name: postgresdb3
3
+ Version: 2.0.0
4
+ Summary: High-Performance Sync and Async PostgreSQL ORM for Python
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 - High-Performance Python ORM
24
+
25
+ **PostgresDB3** — bu PostgreSQL uchun mo'ljallangan zamonaviy, tezkor va ham **sinxron**, ham **asinxron** ishlash imkoniyatiga ega bo'lgan ORM (Object-Relational Mapping) kutubxonasi.
26
+
27
+ U Django ORM kabi qulay va oson tushuniladi, lekin o'zida asinxronlik (`asyncpg`) va ko'plab yuqori texnologik xususiyatlarni mujassamlashtirgan. Kichik botlardan tortib, o'ta katta masshtabdagi (Highload) loyihalargacha bemalol ishlata olasiz!
28
+
29
+ ---
30
+
31
+ ## 📦 O'rnatish
32
+
33
+ Kutubxonani o'rnatish uchun terminalda quyidagi buyruqni kiriting:
34
+
35
+ ```bash
36
+ pip install postgresdb3
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 🚀 Yangi (v2.0) Imkoniyatlari
42
+
43
+ Ushbu "Enterprise" versiyada loyihaga quyidagi gigant imkoniyatlar qo'shildi:
44
+ - **`values()` va `values_list()`** - Xotirani tejash uchun to'g'ridan to'g'ri `dict` yoki `list` qaytarish.
45
+ - **`get_or_create()` va `update_or_create()`** - Kodlarni sezilarli darajada qisqartirish.
46
+ - **`__` orqali Auto-Join** - Munosabatlarni avtomatik bog'lash (Masalan: `filter(user__profile__age__gt=18)`).
47
+ - **Mavhum Modellar (Abstract Models)** - Qayta-qayta kod yozmaslik uchun `class Meta: abstract = True`.
48
+ - **`annotate()` va `aggregate()`** - Murakkab matematik va guruhlash amallari.
49
+ - **`update()` va `delete()` (Ommaviy)** - Obyektlarni bazada bitta qatorda ommaviy tahrirlash yoki o'chirish.
50
+
51
+ ---
52
+
53
+ ## ⚡ Tezkor Boshlash
54
+
55
+ PostgresDB3 bir vaqtning o'zida ikkita dunyoni qo'llab-quvvatlaydi. Xohlasangiz **Sinxron**, xohlasangiz **Asinxron** loyihalarda bir xil sintaksis bilan ishlay olasiz.
56
+
57
+ ### 1. Ulanishni sozlash
58
+
59
+ ```python
60
+ from postgresdb3 import PostgresDB, AsyncPostgresDB
61
+
62
+ # Sinxron ulanish (psycopg2 asosida)
63
+ db_sync = PostgresDB("my_db", "user", "password", host="localhost", port=5432, echo=True)
64
+
65
+ # Asinxron ulanish (asyncpg asosida)
66
+ db_async = AsyncPostgresDB("my_db", "user", "password", host="localhost", port=5432, echo=True)
67
+ ```
68
+ *(Eslatma: `echo=True` yoqilsa, barcha bajarilayotgan SQL so'rovlar terminalda ko'rinib turadi, bu debug uchun juda foydali).*
69
+
70
+ ### 2. Modellarni Yaratish (Abstract Model namunasida)
71
+
72
+ ```python
73
+ from postgresdb3.orm.models import Model, AsyncModel
74
+ from postgresdb3 import String, Integer, Float, Timestamp
75
+
76
+ Model.db = db_sync
77
+
78
+ class TimestampedModel(Model):
79
+ class Meta:
80
+ abstract = True # Jadval yaratilmaydi
81
+
82
+ created_at = Timestamp(auto_now_add=True)
83
+
84
+ class User(TimestampedModel):
85
+ name = String(length=50)
86
+ age = Integer(default=18)
87
+ score = Float(default=0.0)
88
+
89
+ class Meta:
90
+ unique_together = (("name", "age"),) # Ism va yosh bir xil takrorlanmasligi kerak
91
+ index_together = (("score",),) # Qidiruvni tezlashtirish uchun index
92
+ ```
93
+
94
+ ### 3. CRUD Operatsiyalari
95
+
96
+ **Yangi funksiyalar yordamida:**
97
+ ```python
98
+ # Yaratish yoki o'qish (Bazada bo'lmasa yaratadi)
99
+ user, created = User.get_or_create(name="Ali", defaults={'age': 25, 'score': 10.0})
100
+
101
+ # Ommaviy yangilash (Xotirani to'ldirmasdan)
102
+ User.query().filter(age__lt=18).update(score=0.0)
103
+
104
+ # Ommaviy o'chirish
105
+ User.query().filter(score=0.0).delete()
106
+ ```
107
+
108
+ **Tezkor O'qish (Values & Values List):**
109
+ Agar modellar bilan ishlash shart bo'lmasa va faqat JSON yuborish kerak bo'lsa:
110
+ ```python
111
+ users_dict = User.query().values('id', 'name').all()
112
+ # [{'id': 1, 'name': 'Ali'}, {'id': 2, 'name': 'Vali'}]
113
+
114
+ names_list = User.query().values_list('name', flat=True).all()
115
+ # ['Ali', 'Vali']
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 🔄 Migratsiya Tizimi (CLI)
121
+
122
+ Siz o'z loyihangiz papkasida bitta `manage.py` yaratib olib, jadvallarni SQL o'qib-o'tirmasdan terminal orqali avtomatik yangilab borishingiz mumkin.
123
+
124
+ **`manage.py`:**
125
+ ```python
126
+ import sys
127
+ from postgresdb3 import execute_from_command_line
128
+ from myapp.models import db_sync # Sizning bazaga ulangan db obyektingiz
129
+
130
+ # Modellar dvigatelga ko'rinishi uchun barchasini import qilish SHART!
131
+ from myapp.models import User, Post
132
+
133
+ if __name__ == "__main__":
134
+ execute_from_command_line(db_sync, sys.argv)
135
+ ```
136
+
137
+ **Terminalda:**
138
+ ```bash
139
+ # Modellar asosida migratsiya faylini tayyorlash
140
+ python manage.py makemigrations initial_setup
141
+
142
+ # Bazaga jadvallarni qo'shish
143
+ python manage.py migrate
144
+
145
+ # Oxirgi migratsiyani bekor qilish va qaytarish (Rollback)
146
+ python manage.py undo
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 🔍 Murakkab Qidiruv va Aloqalar (Relations)
152
+
153
+ **Auto-Join (Bog'langan jadvallar bo'ylab o'tish):**
154
+ Qo'lda JOIN yozish o'rniga, qo'shaloq chiziqcha `__` ishlating:
155
+ ```python
156
+ posts = Post.query().filter(author__name__startswith='A').all()
157
+ ```
158
+
159
+ **Q va F ifodalar (Murakkab shartlar):**
160
+ ```python
161
+ from postgresdb3 import Q, F
162
+
163
+ # OR (|) sharti
164
+ User.query().filter(Q(age__gt=20) | Q(name="Ali")).all()
165
+
166
+ # Yoshiga nisbatan score'i katta bo'lganlarni izlash
167
+ User.query().filter(score__gt=F("age")).all()
168
+ ```
169
+
170
+ **N+1 xatoligini chetlab o'tish (select_related & prefetch_related):**
171
+ ```python
172
+ # ForeignKey uchun (1 ta SQL so'rov)
173
+ posts = Post.query().select_related("author").all()
174
+
175
+ # ManyToMany uchun (Optimallashtirilgan yig'ish)
176
+ posts = Post.query().prefetch_related("tags").all()
177
+ ```
178
+
179
+ ---
180
+
181
+ ## 📊 Katta Ma'lumotlar bilan Ishlash (Bulk & Pagination)
182
+
183
+ **Sahifalash (Pagination):**
184
+ Foydalanuvchiga ma'lumotlarni qismlab taqdim etish:
185
+ ```python
186
+ result = User.query().paginate(page=1, per_page=20)
187
+ print(result['total']) # Jami elementlar soni
188
+ print(result['has_next']) # Keyingi sahifa bormi?
189
+ print(result['data']) # Modellar ro'yxati (20 ta)
190
+ ```
191
+
192
+ **Ommaviy Yozish (Bulk Create):**
193
+ 10,000 ta obyekti birma-bir emas, bitta so'rov orqali kiritish:
194
+ ```python
195
+ users = [User(name=f"U-{i}", age=20) for i in range(10000)]
196
+ User.bulk_create(users)
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 📈 Agregatsiya va Annotatsiya (GroupBy)
202
+
203
+ Baza orqali kalkulyatsiyalarni amalga oshirish:
204
+
205
+ ```python
206
+ from postgresdb3 import Sum, Avg, Count
207
+
208
+ # Jami obshiy statiska:
209
+ result = User.query().aggregate(
210
+ total_score=Sum("score"),
211
+ avg_age=Avg("age")
212
+ )
213
+ # {'total_score': 1500, 'avg_age': 25}
214
+
215
+ # Guruhlab hisoblash (Masalan: Har bir userning postlari soni):
216
+ users_with_count = User.query().annotate(post_count=Count("posts")).all()
217
+ for u in users_with_count:
218
+ print(u.name, u.post_count)
219
+ ```
220
+
221
+ ---
222
+
223
+ *Ushbu ORM kutubxonasi Python olamidagi ilg'or texnologiyalar yordamida tezlik va barqarorlikni birinchi o'ringa qo'yuvchi mutaxassislar uchun maxsus qurilgan.*
@@ -0,0 +1,201 @@
1
+ # PostgresDB3 - High-Performance Python ORM
2
+
3
+ **PostgresDB3** — bu PostgreSQL uchun mo'ljallangan zamonaviy, tezkor va ham **sinxron**, ham **asinxron** ishlash imkoniyatiga ega bo'lgan ORM (Object-Relational Mapping) kutubxonasi.
4
+
5
+ U Django ORM kabi qulay va oson tushuniladi, lekin o'zida asinxronlik (`asyncpg`) va ko'plab yuqori texnologik xususiyatlarni mujassamlashtirgan. Kichik botlardan tortib, o'ta katta masshtabdagi (Highload) loyihalargacha bemalol ishlata olasiz!
6
+
7
+ ---
8
+
9
+ ## 📦 O'rnatish
10
+
11
+ Kutubxonani o'rnatish uchun terminalda quyidagi buyruqni kiriting:
12
+
13
+ ```bash
14
+ pip install postgresdb3
15
+ ```
16
+
17
+ ---
18
+
19
+ ## 🚀 Yangi (v2.0) Imkoniyatlari
20
+
21
+ Ushbu "Enterprise" versiyada loyihaga quyidagi gigant imkoniyatlar qo'shildi:
22
+ - **`values()` va `values_list()`** - Xotirani tejash uchun to'g'ridan to'g'ri `dict` yoki `list` qaytarish.
23
+ - **`get_or_create()` va `update_or_create()`** - Kodlarni sezilarli darajada qisqartirish.
24
+ - **`__` orqali Auto-Join** - Munosabatlarni avtomatik bog'lash (Masalan: `filter(user__profile__age__gt=18)`).
25
+ - **Mavhum Modellar (Abstract Models)** - Qayta-qayta kod yozmaslik uchun `class Meta: abstract = True`.
26
+ - **`annotate()` va `aggregate()`** - Murakkab matematik va guruhlash amallari.
27
+ - **`update()` va `delete()` (Ommaviy)** - Obyektlarni bazada bitta qatorda ommaviy tahrirlash yoki o'chirish.
28
+
29
+ ---
30
+
31
+ ## ⚡ Tezkor Boshlash
32
+
33
+ PostgresDB3 bir vaqtning o'zida ikkita dunyoni qo'llab-quvvatlaydi. Xohlasangiz **Sinxron**, xohlasangiz **Asinxron** loyihalarda bir xil sintaksis bilan ishlay olasiz.
34
+
35
+ ### 1. Ulanishni sozlash
36
+
37
+ ```python
38
+ from postgresdb3 import PostgresDB, AsyncPostgresDB
39
+
40
+ # Sinxron ulanish (psycopg2 asosida)
41
+ db_sync = PostgresDB("my_db", "user", "password", host="localhost", port=5432, echo=True)
42
+
43
+ # Asinxron ulanish (asyncpg asosida)
44
+ db_async = AsyncPostgresDB("my_db", "user", "password", host="localhost", port=5432, echo=True)
45
+ ```
46
+ *(Eslatma: `echo=True` yoqilsa, barcha bajarilayotgan SQL so'rovlar terminalda ko'rinib turadi, bu debug uchun juda foydali).*
47
+
48
+ ### 2. Modellarni Yaratish (Abstract Model namunasida)
49
+
50
+ ```python
51
+ from postgresdb3.orm.models import Model, AsyncModel
52
+ from postgresdb3 import String, Integer, Float, Timestamp
53
+
54
+ Model.db = db_sync
55
+
56
+ class TimestampedModel(Model):
57
+ class Meta:
58
+ abstract = True # Jadval yaratilmaydi
59
+
60
+ created_at = Timestamp(auto_now_add=True)
61
+
62
+ class User(TimestampedModel):
63
+ name = String(length=50)
64
+ age = Integer(default=18)
65
+ score = Float(default=0.0)
66
+
67
+ class Meta:
68
+ unique_together = (("name", "age"),) # Ism va yosh bir xil takrorlanmasligi kerak
69
+ index_together = (("score",),) # Qidiruvni tezlashtirish uchun index
70
+ ```
71
+
72
+ ### 3. CRUD Operatsiyalari
73
+
74
+ **Yangi funksiyalar yordamida:**
75
+ ```python
76
+ # Yaratish yoki o'qish (Bazada bo'lmasa yaratadi)
77
+ user, created = User.get_or_create(name="Ali", defaults={'age': 25, 'score': 10.0})
78
+
79
+ # Ommaviy yangilash (Xotirani to'ldirmasdan)
80
+ User.query().filter(age__lt=18).update(score=0.0)
81
+
82
+ # Ommaviy o'chirish
83
+ User.query().filter(score=0.0).delete()
84
+ ```
85
+
86
+ **Tezkor O'qish (Values & Values List):**
87
+ Agar modellar bilan ishlash shart bo'lmasa va faqat JSON yuborish kerak bo'lsa:
88
+ ```python
89
+ users_dict = User.query().values('id', 'name').all()
90
+ # [{'id': 1, 'name': 'Ali'}, {'id': 2, 'name': 'Vali'}]
91
+
92
+ names_list = User.query().values_list('name', flat=True).all()
93
+ # ['Ali', 'Vali']
94
+ ```
95
+
96
+ ---
97
+
98
+ ## 🔄 Migratsiya Tizimi (CLI)
99
+
100
+ Siz o'z loyihangiz papkasida bitta `manage.py` yaratib olib, jadvallarni SQL o'qib-o'tirmasdan terminal orqali avtomatik yangilab borishingiz mumkin.
101
+
102
+ **`manage.py`:**
103
+ ```python
104
+ import sys
105
+ from postgresdb3 import execute_from_command_line
106
+ from myapp.models import db_sync # Sizning bazaga ulangan db obyektingiz
107
+
108
+ # Modellar dvigatelga ko'rinishi uchun barchasini import qilish SHART!
109
+ from myapp.models import User, Post
110
+
111
+ if __name__ == "__main__":
112
+ execute_from_command_line(db_sync, sys.argv)
113
+ ```
114
+
115
+ **Terminalda:**
116
+ ```bash
117
+ # Modellar asosida migratsiya faylini tayyorlash
118
+ python manage.py makemigrations initial_setup
119
+
120
+ # Bazaga jadvallarni qo'shish
121
+ python manage.py migrate
122
+
123
+ # Oxirgi migratsiyani bekor qilish va qaytarish (Rollback)
124
+ python manage.py undo
125
+ ```
126
+
127
+ ---
128
+
129
+ ## 🔍 Murakkab Qidiruv va Aloqalar (Relations)
130
+
131
+ **Auto-Join (Bog'langan jadvallar bo'ylab o'tish):**
132
+ Qo'lda JOIN yozish o'rniga, qo'shaloq chiziqcha `__` ishlating:
133
+ ```python
134
+ posts = Post.query().filter(author__name__startswith='A').all()
135
+ ```
136
+
137
+ **Q va F ifodalar (Murakkab shartlar):**
138
+ ```python
139
+ from postgresdb3 import Q, F
140
+
141
+ # OR (|) sharti
142
+ User.query().filter(Q(age__gt=20) | Q(name="Ali")).all()
143
+
144
+ # Yoshiga nisbatan score'i katta bo'lganlarni izlash
145
+ User.query().filter(score__gt=F("age")).all()
146
+ ```
147
+
148
+ **N+1 xatoligini chetlab o'tish (select_related & prefetch_related):**
149
+ ```python
150
+ # ForeignKey uchun (1 ta SQL so'rov)
151
+ posts = Post.query().select_related("author").all()
152
+
153
+ # ManyToMany uchun (Optimallashtirilgan yig'ish)
154
+ posts = Post.query().prefetch_related("tags").all()
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 📊 Katta Ma'lumotlar bilan Ishlash (Bulk & Pagination)
160
+
161
+ **Sahifalash (Pagination):**
162
+ Foydalanuvchiga ma'lumotlarni qismlab taqdim etish:
163
+ ```python
164
+ result = User.query().paginate(page=1, per_page=20)
165
+ print(result['total']) # Jami elementlar soni
166
+ print(result['has_next']) # Keyingi sahifa bormi?
167
+ print(result['data']) # Modellar ro'yxati (20 ta)
168
+ ```
169
+
170
+ **Ommaviy Yozish (Bulk Create):**
171
+ 10,000 ta obyekti birma-bir emas, bitta so'rov orqali kiritish:
172
+ ```python
173
+ users = [User(name=f"U-{i}", age=20) for i in range(10000)]
174
+ User.bulk_create(users)
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 📈 Agregatsiya va Annotatsiya (GroupBy)
180
+
181
+ Baza orqali kalkulyatsiyalarni amalga oshirish:
182
+
183
+ ```python
184
+ from postgresdb3 import Sum, Avg, Count
185
+
186
+ # Jami obshiy statiska:
187
+ result = User.query().aggregate(
188
+ total_score=Sum("score"),
189
+ avg_age=Avg("age")
190
+ )
191
+ # {'total_score': 1500, 'avg_age': 25}
192
+
193
+ # Guruhlab hisoblash (Masalan: Har bir userning postlari soni):
194
+ users_with_count = User.query().annotate(post_count=Count("posts")).all()
195
+ for u in users_with_count:
196
+ print(u.name, u.post_count)
197
+ ```
198
+
199
+ ---
200
+
201
+ *Ushbu ORM kutubxonasi Python olamidagi ilg'or texnologiyalar yordamida tezlik va barqarorlikni birinchi o'ringa qo'yuvchi mutaxassislar uchun maxsus qurilgan.*
@@ -19,6 +19,7 @@ def execute_from_command_line(db, argv=None):
19
19
 
20
20
  make_parser = subparsers.add_parser("makemigrations", help="Modellardagi o'zgarishlar asosida yangi migratsiya fayllarini yaratadi")
21
21
  make_parser.add_argument("name", nargs="?", default="auto", help="Migratsiya nomi (masalan: initial_setup)")
22
+ make_parser.add_argument("--no-input", action="store_true", help="O'chirishlar (DROP) bo'lsa ogohlantirmasdan bajarish")
22
23
 
23
24
 
24
25
  subparsers.add_parser("migrate", help="Mavjud migratsiyalarni ma'lumotlar bazasiga qo'llaydi")
@@ -29,19 +30,22 @@ def execute_from_command_line(db, argv=None):
29
30
  if args.command == "makemigrations":
30
31
  engine = MigrationEngine()
31
32
  print(f"'{args.name}' nomli migratsiya yaratilmoqda...")
32
- engine.makemigrations(args.name)
33
+ engine.makemigrations(args.name, interactive=not args.no_input)
33
34
 
34
35
  elif args.command == "migrate":
35
36
  engine = MigrationEngine()
36
37
  if isinstance(db, PostgresDB):
37
38
  engine.migrate(db)
38
39
  elif isinstance(db, AsyncPostgresDB):
39
-
40
- loop = asyncio.get_event_loop()
41
- if loop.is_running():
40
+ try:
41
+ loop = asyncio.get_running_loop()
42
+ except RuntimeError:
43
+ loop = None
44
+
45
+ if loop and loop.is_running():
42
46
  asyncio.ensure_future(engine.async_migrate(db))
43
47
  else:
44
- loop.run_until_complete(engine.async_migrate(db))
48
+ asyncio.run(engine.async_migrate(db))
45
49
  else:
46
50
  print("Xato: Noma'lum DB obyekti (PostgresDB yoki AsyncPostgresDB kerak).")
47
51
 
@@ -50,11 +54,15 @@ def execute_from_command_line(db, argv=None):
50
54
  if isinstance(db, PostgresDB):
51
55
  engine.undo_migration(db)
52
56
  elif isinstance(db, AsyncPostgresDB):
53
- loop = asyncio.get_event_loop()
54
- if loop.is_running():
57
+ try:
58
+ loop = asyncio.get_running_loop()
59
+ except RuntimeError:
60
+ loop = None
61
+
62
+ if loop and loop.is_running():
55
63
  asyncio.ensure_future(engine.async_undo_migration(db))
56
64
  else:
57
- loop.run_until_complete(engine.async_undo_migration(db))
65
+ asyncio.run(engine.async_undo_migration(db))
58
66
  else:
59
67
  print("Xato: Noma'lum DB obyekti (PostgresDB yoki AsyncPostgresDB kerak).")
60
68
 
@@ -27,6 +27,17 @@ class AsyncPostgresDB:
27
27
  self._async_conn = contextvars.ContextVar(f"async_conn_{id(self)}", default=None)
28
28
 
29
29
  async def _manager(self, sql: str, *params, fetchone=False, fetchall=False, commit=False, many=False) -> Any:
30
+ if params and "%s" in sql:
31
+ import re
32
+ counter = 1
33
+ def repl(match):
34
+ nonlocal counter
35
+ res = f"${counter}"
36
+ counter += 1
37
+ return res
38
+ sql = re.sub(r'(?<!%)%s', repl, sql)
39
+ sql = sql.replace("%%", "%")
40
+
30
41
  if self.echo:
31
42
  print(f"\033[94m[SQL]: {sql} \n[PARAMS]: {params}\033[0m")
32
43
  if not self.pool:
@@ -99,6 +110,9 @@ class AsyncPostgresDB:
99
110
  if not isinstance(columns, str) or not columns.strip():
100
111
  raise ValueError("columns noto'g'ri bo'lishi mumkin emas")
101
112
 
113
+ if ";" in columns or "--" in columns:
114
+ raise ValueError("SQL injection xavfi mavjud: yaroqsiz ustun nomi")
115
+
102
116
  return columns
103
117
 
104
118
  def _build_where(self, where: Any, start_index: int = 1) -> tuple[str, list]:
@@ -124,6 +138,12 @@ class AsyncPostgresDB:
124
138
  "lte": "<=",
125
139
  "like": "LIKE",
126
140
  "ilike": "ILIKE",
141
+ "contains": "LIKE",
142
+ "icontains": "ILIKE",
143
+ "startswith": "LIKE",
144
+ "istartswith": "ILIKE",
145
+ "endswith": "LIKE",
146
+ "iendswith": "ILIKE",
127
147
  }
128
148
 
129
149
  for key, value in where.items():
@@ -135,6 +155,12 @@ class AsyncPostgresDB:
135
155
  self._validate_identifier(field, "where field")
136
156
 
137
157
  if op in operator_map:
158
+ if op in ("contains", "icontains"):
159
+ value = f"%{value}%"
160
+ elif op in ("startswith", "istartswith"):
161
+ value = f"{value}%"
162
+ elif op in ("endswith", "iendswith"):
163
+ value = f"%{value}"
138
164
  clauses.append(f"{field} {operator_map[op]} ${index}")
139
165
  params.append(value)
140
166
  index += 1
@@ -294,18 +320,9 @@ class AsyncPostgresDB:
294
320
  if not values_list:
295
321
  raise ValueError("values_list bo'sh bo'lishi mumkin emas")
296
322
 
297
- placeholders_list = []
298
- flat_values = []
299
- counter = 1
300
-
301
- for values in values_list:
302
- placeholders = ", ".join(f"${i}" for i in range(counter, counter + len(values)))
303
- placeholders_list.append(f"({placeholders})")
304
- flat_values.extend(values)
305
- counter += len(values)
306
-
307
- sql = f"INSERT INTO {table} ({columns}) VALUES {', '.join(placeholders_list)}"
308
- await self._manager(sql, *flat_values, commit=True)
323
+ placeholders = ", ".join(f"${i+1}" for i in range(len(values_list[0])))
324
+ sql = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
325
+ await self._manager(sql, values_list, commit=True, many=True)
309
326
 
310
327
  async def update(self, table: str, set_column: str, set_value: Any, where_column: str, where_value: Any) -> None:
311
328
  sql = f"UPDATE {table} SET {set_column} = $1 WHERE {where_column} = $2"
@@ -459,6 +476,11 @@ class AsyncPostgresDB:
459
476
 
460
477
  @asynccontextmanager
461
478
  async def transaction(self):
479
+ existing_conn = self._async_conn.get()
480
+ if existing_conn:
481
+ yield existing_conn
482
+ return
483
+
462
484
  if not self.pool:
463
485
  if self._pool_lock is None:
464
486
  self._pool_lock = asyncio.Lock()
@@ -473,7 +495,7 @@ class AsyncPostgresDB:
473
495
  min_size=self.min_size,
474
496
  max_size=self.max_size
475
497
  )
476
-
498
+
477
499
  conn = await self.pool.acquire()
478
500
  token = self._async_conn.set(conn)
479
501
  try:
@@ -482,3 +504,15 @@ class AsyncPostgresDB:
482
504
  finally:
483
505
  self._async_conn.reset(token)
484
506
  await self.pool.release(conn)
507
+ def atomic(self):
508
+ """
509
+ Asinxron funksiyalarni tranzaksiya (transaction) ichida ishlatish uchun dekorator.
510
+ """
511
+ def decorator(func):
512
+ import functools
513
+ @functools.wraps(func)
514
+ async def wrapper(*args, **kwargs):
515
+ async with self.transaction():
516
+ return await func(*args, **kwargs)
517
+ return wrapper
518
+ return decorator