در این مطلب و مطالب آتی اصلاً کاری نداریم که ORM چیست و چرا باید از آن استفاده کرد. بلکه فقط قصد بر این است که هر query که وجود دارد معادل آن را با Django ORM پیاده کنیم. ممکن است خواننده این شماره بگوید: “خیلی از مسایل را به طور پیشفرض به خود خواننده واگذار کرده است”. اما قرار نیست از شماره بعد چنین باشد.
برای dataset از dataset کتاب Sams Teach Yourlself sql in 24 hours استفاده شده است که البته این dataset نواقصی دارد، اما برای مثالهای ما کفایت میکند. این dataset را میتوانید از اینجا دانلود کنید.
ایجاد مدل:
نکته: یک پروژه بسازید و در آن یک app با نام sql بسازید.
نکته: فایلهای دانلود شده اولی برای ایجاد tableها و دومی برای INSERT کردن دادهها میباشند.
نکته: ممکن است بگویید models.py را چگونه بسازم. فرمان manage.py یک command دارد با نام inspectdb که مدل مربوط به یک DB و یا یک Table را برایتان پرینت میکند.
1 |
./manage.py inspectdb |
نکته: فیلد managed را به True تغییر دهید تا بتواند برایتان جداول را ایجاد کند.
برای models.py آن کلاسهای زیر را تعریف کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
from django.db import models class CustomerTbl(models.Model): cust_id = models.CharField(db_column='CUST_ID', primary_key=True, max_length=10) # Field name made lowercase. cust_name = models.CharField(db_column='CUST_NAME', max_length=30) # Field name made lowercase. cust_address = models.CharField(db_column='CUST_ADDRESS', max_length=20) # Field name made lowercase. cust_city = models.CharField(db_column='CUST_CITY', max_length=15) # Field name made lowercase. cust_state = models.CharField(db_column='CUST_STATE', max_length=2) # Field name made lowercase. cust_zip = models.IntegerField(db_column='CUST_ZIP') # Field name made lowerscase. cust_phone = models.CharField(db_column='CUST_PHONE', max_length=10, blank=True, null=True) # Field name made lowercase. cust_fax = models.CharField(db_column='CUST_FAX', max_length=10, blank=True, null=True) # Field name made lowercase. class Meta: managed = True db_table = 'CUSTOMER_TBL' class EmployeePayTbl(models.Model): emp = models.OneToOneField('EmployeeTbl', models.DO_NOTHING, db_column='EMP_ID', primary_key=True) # Field name made lowercase. position = models.CharField(db_column='POSITION', max_length=15) # Field name made lowercase. date_hire = models.DateField(db_column='DATE_HIRE', blank=True, null=True) # Field name made lowercase. pay_rate = models.DecimalField(db_column='PAY_RATE', max_digits=4, decimal_places=2, blank=True, null=True) # Field name made lowercase. date_last_raise = models.DateField(db_column='DATE_LAST_RAISE', blank=True, null=True) # Field name made lowercase. salary = models.DecimalField(db_column='SALARY', max_digits=8, decimal_places=2, blank=True, null=True) # Field name made lowercase. bonus = models.DecimalField(db_column='BONUS', max_digits=6, decimal_places=2, blank=True, null=True) # Field name made lowercase. class Meta: managed = True db_table = 'EMPLOYEE_PAY_TBL' class EmployeeTbl(models.Model): emp_id = models.CharField(db_column='EMP_ID', primary_key=True, max_length=9) # Field name made lowercase. last_name = models.CharField(db_column='LAST_NAME', max_length=15) # Field name made lowercase. first_name = models.CharField(db_column='FIRST_NAME', max_length=15) # Field name made lowercase. middle_name = models.CharField(db_column='MIDDLE_NAME', max_length=15, blank=True, null=True) # Field name made lowercase. address = models.CharField(db_column='ADDRESS', max_length=30) # Field name made lowercase. city = models.CharField(db_column='CITY', max_length=15) # Field name made lowercase. state = models.CharField(db_column='STATE', max_length=2) # Field name made lowercase. zip = models.IntegerField(db_column='ZIP') # Field name made lowercase. phone = models.CharField(db_column='PHONE', max_length=10, blank=True, null=True) # Field name made lowercase. pager = models.CharField(db_column='PAGER', max_length=10, blank=True, null=True) # Field name made lowercase. class Meta: managed = True db_table = 'EMPLOYEE_TBL' class OrdersTbl(models.Model): ord_num = models.CharField(db_column='ORD_NUM', primary_key=True, max_length=10) # Field name made lowercase. cust_id = models.CharField(db_column='CUST_ID', max_length=10) # Field name made lowercase. prod_id = models.CharField(db_column='PROD_ID', max_length=10) # Field name made lowercase. qty = models.IntegerField(db_column='QTY') # Field name made lowercase. ord_date = models.DateField(db_column='ORD_DATE', blank=True, null=True) # Field name made lowercase. class Meta: managed = True db_table = 'ORDERS_TBL' class ProductsTbl(models.Model): prod_id = models.CharField(db_column='PROD_ID', primary_key=True, max_length=10) # Field name made lowercase. prod_desc = models.CharField(db_column='PROD_DESC', max_length=40) # Field name made lowercase. cost = models.DecimalField(db_column='COST', max_digits=6, decimal_places=2) # Field name made lowercase. class Meta: managed = True db_table = 'PRODUCTS_TBL' |
سپس با فرمانهای زیر جداول را ایجاد نمایید:
1 2 |
./manage.py makemigrations sql ./manage.py migrate sql |
نکته: فرض بر این است که با ساخت جدول و تغییرات آن در خود Django آشنا هستید و نیازی به توضیح فایل models.py نیست.
نکته: فرض دیگر این است که با SQL به صورت کامل آشنا هستید.
نکته: فرض دیگر بر این است که شما با فایل settings.py کاملاً آشنایی دارید و هر چیزی را میتوانید setup کنید.
نکته: فرض دیگر این است که شما با virtualenv آشنا هستید و میتوانید آن را فعال و غیر فعال کنید.
آشنایی با QuerySet:
QuerySet یک data type با نام django.db.models.query.QuerySet است که مقادیر برگردانده شده از پایگاه داده را بر میگرداند. اگر یک نوع یک QuerySet را با تابع type پرینت بگیرید این چنین میشود:
1 |
print(type(EmployeePayTbl.objects.all()) |
1 |
<class 'django.db.models.query.QuerySet'> |
نکته: ممکن است سوال پیش بیایید که EmployeePayTbl چیست. یکی از کلاسهای فایل models.py میباشد، که اشاره به جدول EMPLOYEE_PAY_TBL دارد.
اگر تابع بالا را پرینت بگیریم، خروجیاش نیز زیر آن آمده است.
1 |
print(EmployeePayTbl.objects.all()) |
1 |
<QuerySet [<EmployeePayTbl: EmployeePayTbl object (213764555)>, <EmployeePayTbl: EmployeePayTbl object (220984332)>, <EmployeePayTbl: EmployeePayTbl object (311549902)>, <EmployeePayTbl: EmployeePayTbl object (313782439)>, <EmployeePayTbl: EmployeePayTbl object (442346889)>, <EmployeePayTbl: EmployeePayTbl object (443679012)>]> |
اگر در شل SQL از جدول مذکور Select بگیریم، نتیجه زیر حاصل میشود:
1 2 3 4 5 6 7 8 9 10 11 |
MariaDB [esql]> select * from EMPLOYEE_PAY_TBL; +-----------+---------------+------------+----------+-----------------+----------+---------+ | EMP_ID | POSITION | DATE_HIRE | PAY_RATE | DATE_LAST_RAISE | SALARY | BONUS | +-----------+---------------+------------+----------+-----------------+----------+---------+ | 213764555 | SALES MANAGER | 2004-08-14 | NULL | 2009-08-01 | 30000.00 | 2000.00 | | 220984332 | SHIPPER | 2006-07-22 | 11.00 | 1999-07-01 | NULL | NULL | | 311549902 | MARKETING | 1999-05-23 | NULL | 2009-05-01 | 40000.00 | NULL | | 313782439 | SALESMAN | 2007-06-28 | NULL | NULL | 20000.00 | 1000.00 | | 442346889 | TEAM LEADER | 2000-06-17 | 14.75 | 2009-06-01 | NULL | NULL | | 443679012 | SHIPPER | 2001-01-14 | 15.00 | 1999-01-01 | NULL | NULL | +-----------+---------------+------------+----------+-----------------+----------+---------+ |
سوالی که مطرح میشود این است که چرا فقط فیلد اول در QuerySet پرینت میشود. در پایتون تابعی با نام __str__ وجود دارد که میتواند داخل هر کلاسی override شود و نوع نمایش string خروجی را مشخص میکند. فرض میکنیم که بخواهیم فیلد POSITION را بخواهیم نمایش دهیم. در نتیجه این تابع را به کلاس اضافه میکنیم:
1 2 |
def __str__(self): return str(self.position) |
خروجی به صورت زیر میشود:
1 |
<QuerySet [<EmployeePayTbl: SALES MANAGER>, <EmployeePayTbl: SHIPPER>, <EmployeePayTbl: MARKETING>, <EmployeePayTbl: SALESMAN>, <EmployeePayTbl: TEAM LEADER>, <EmployeePayTbl: SHIPPER>]> |
نکته: تابع all که در بالا مثال زده شد، یک QuerySet بر میگرداند که کل row های شما را شامل میشود.
به تکه کد زیر دقت کنید:
1 2 |
for row in EmployeePayTbl.objects.all(): print(row) |
کل row های جدول برگشته شده را پرینت میکند:
1 2 3 4 5 6 7 |
SALES MANAGER SHIPPER MARKETING SALESMAN TEAM LEADER SHIPPER |
در مطالب بعدی به صورت جدی به پیادهسازی انواع query در Django میپردازیم.
مطالب مربوط به ORM جنگو ادامه خواهند داشت. منتظر باشید.