postgresdb3 1.1.0__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.
- postgresdb3-2.0.0/PKG-INFO +223 -0
- postgresdb3-2.0.0/README.md +201 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/cli.py +16 -8
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/core/async_db.py +46 -16
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/core/sync_db.py +64 -6
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/migrations/engine.py +105 -13
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/base.py +17 -5
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/fields/base.py +7 -0
- postgresdb3-2.0.0/postgresdb3/orm/fields/text.py +23 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/meta.py +16 -6
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/models.py +82 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/query.py +275 -22
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/relations.py +17 -28
- postgresdb3-2.0.0/postgresdb3.egg-info/PKG-INFO +223 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/setup.py +1 -1
- postgresdb3-1.1.0/PKG-INFO +0 -303
- postgresdb3-1.1.0/README.md +0 -281
- postgresdb3-1.1.0/postgresdb3/orm/fields/text.py +0 -15
- postgresdb3-1.1.0/postgresdb3.egg-info/PKG-INFO +0 -303
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/__init__.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/core/__init__.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/migrations/__init__.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/__init__.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/expressions.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/fields/__init__.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/fields/datetime.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/fields/foreign.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/fields/misc.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3/orm/fields/numeric.py +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3.egg-info/SOURCES.txt +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3.egg-info/dependency_links.txt +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3.egg-info/requires.txt +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/postgresdb3.egg-info/top_level.txt +0 -0
- {postgresdb3-1.1.0 → postgresdb3-2.0.0}/pyproject.toml +0 -0
- {postgresdb3-1.1.0 → 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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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,9 +27,16 @@ 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 "%s" in sql:
|
|
31
|
-
|
|
32
|
-
|
|
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("%%", "%")
|
|
33
40
|
|
|
34
41
|
if self.echo:
|
|
35
42
|
print(f"\033[94m[SQL]: {sql} \n[PARAMS]: {params}\033[0m")
|
|
@@ -103,6 +110,9 @@ class AsyncPostgresDB:
|
|
|
103
110
|
if not isinstance(columns, str) or not columns.strip():
|
|
104
111
|
raise ValueError("columns noto'g'ri bo'lishi mumkin emas")
|
|
105
112
|
|
|
113
|
+
if ";" in columns or "--" in columns:
|
|
114
|
+
raise ValueError("SQL injection xavfi mavjud: yaroqsiz ustun nomi")
|
|
115
|
+
|
|
106
116
|
return columns
|
|
107
117
|
|
|
108
118
|
def _build_where(self, where: Any, start_index: int = 1) -> tuple[str, list]:
|
|
@@ -128,6 +138,12 @@ class AsyncPostgresDB:
|
|
|
128
138
|
"lte": "<=",
|
|
129
139
|
"like": "LIKE",
|
|
130
140
|
"ilike": "ILIKE",
|
|
141
|
+
"contains": "LIKE",
|
|
142
|
+
"icontains": "ILIKE",
|
|
143
|
+
"startswith": "LIKE",
|
|
144
|
+
"istartswith": "ILIKE",
|
|
145
|
+
"endswith": "LIKE",
|
|
146
|
+
"iendswith": "ILIKE",
|
|
131
147
|
}
|
|
132
148
|
|
|
133
149
|
for key, value in where.items():
|
|
@@ -139,6 +155,12 @@ class AsyncPostgresDB:
|
|
|
139
155
|
self._validate_identifier(field, "where field")
|
|
140
156
|
|
|
141
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}"
|
|
142
164
|
clauses.append(f"{field} {operator_map[op]} ${index}")
|
|
143
165
|
params.append(value)
|
|
144
166
|
index += 1
|
|
@@ -298,18 +320,9 @@ class AsyncPostgresDB:
|
|
|
298
320
|
if not values_list:
|
|
299
321
|
raise ValueError("values_list bo'sh bo'lishi mumkin emas")
|
|
300
322
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
for values in values_list:
|
|
306
|
-
placeholders = ", ".join(f"${i}" for i in range(counter, counter + len(values)))
|
|
307
|
-
placeholders_list.append(f"({placeholders})")
|
|
308
|
-
flat_values.extend(values)
|
|
309
|
-
counter += len(values)
|
|
310
|
-
|
|
311
|
-
sql = f"INSERT INTO {table} ({columns}) VALUES {', '.join(placeholders_list)}"
|
|
312
|
-
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)
|
|
313
326
|
|
|
314
327
|
async def update(self, table: str, set_column: str, set_value: Any, where_column: str, where_value: Any) -> None:
|
|
315
328
|
sql = f"UPDATE {table} SET {set_column} = $1 WHERE {where_column} = $2"
|
|
@@ -463,6 +476,11 @@ class AsyncPostgresDB:
|
|
|
463
476
|
|
|
464
477
|
@asynccontextmanager
|
|
465
478
|
async def transaction(self):
|
|
479
|
+
existing_conn = self._async_conn.get()
|
|
480
|
+
if existing_conn:
|
|
481
|
+
yield existing_conn
|
|
482
|
+
return
|
|
483
|
+
|
|
466
484
|
if not self.pool:
|
|
467
485
|
if self._pool_lock is None:
|
|
468
486
|
self._pool_lock = asyncio.Lock()
|
|
@@ -477,7 +495,7 @@ class AsyncPostgresDB:
|
|
|
477
495
|
min_size=self.min_size,
|
|
478
496
|
max_size=self.max_size
|
|
479
497
|
)
|
|
480
|
-
|
|
498
|
+
|
|
481
499
|
conn = await self.pool.acquire()
|
|
482
500
|
token = self._async_conn.set(conn)
|
|
483
501
|
try:
|
|
@@ -486,3 +504,15 @@ class AsyncPostgresDB:
|
|
|
486
504
|
finally:
|
|
487
505
|
self._async_conn.reset(token)
|
|
488
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
|