学习Django的时候,总是觉得这部分内容和实际的应用有一定的差别或者距离。一方面Django自带的ORM对于底层数据库来说是一种适配性很强的组件,可以不强依赖于某一种数据库,sqlite,MySQL,Oracle,PG等等都可以,学习起来需要一定的周期。另外一方面是因为这种方式是通用的API,一下子没有了SQL语句,要理解并接受这种思想,需要一点时间,对很多DBA来说需要适应。第三点就是没有融会贯通,好像看明白了,但是实际写的时候发现还是摸黑,不知道从何入手。
所以我就换个思路,从数据库的角度来反向解析Django怎么实现我们常见的数据需求。先做减法,侧重于说查询的部分。常见的数据需求,这个需求有些大,怎么让他更通用呢,我想到了Oracle里面的emp,dept,自打学习数据库,很多的测试案例就和这两个表分不开,所以我们就从这个为切入点来逐步分析。
有的同学可能开始就打了退堂鼓,Oracle的还要转换语句,还有数据类型,而使用的数据库是MySQL,是不是有些麻烦啊,其实这些都不是事儿,不花一点功夫肯定难有收获。
我们配置下emp,dept的结构,是在Django的models.py的文件中配置即可。
fromdjango.dbimportmodels importdjango.utils.timezoneastimezone classdept(models.Model): deptno=models.AutoField(primary_key=True) dname=models.CharField(max_length=30) loc=models.CharField(max_length=30,default='') classMeta: db_table='dept' verbose_name='DEPT' verbose_name_plural='DEPT' ordering=['deptno'] def__unicode__(self): return'%s%s'%(self.deptno,self.dname) classdept(models.Model): deptno=models.AutoField(primary_key=True) dname=models.CharField(max_length=30) loc=models.CharField(max_length=30,default='') classMeta: db_table='dept' verbose_name='DEPT' verbose_name_plural='DEPT' ordering=['deptno'] def__unicode__(self): return'%s%s'%(self.deptno,self.dname) classemp(models.Model): empno=models.AutoField(primary_key=True) ename=models.CharField(max_length=30) job=models.CharField(max_length=30) mgr=models.IntegerField() hiredate=models.DateTimeField('hiredate',default=timezone.now) sal=models.IntegerField() comm=models.IntegerField deptno=models.ForeignKey('dept') classMeta: db_table='emp' verbose_name='EMP' verbose_name_plural='EMP' verbose_name_plural='EMP' ordering=['empno','ename'] def__unicode__(self): return'%s%s'%(self.empno,self.ename)
其实内容来看倒也不难,类型是通用的。
使用python manage.py makemigrations得到变化的结构和数据
Migrationsfor'scott': 0001_initial.py: -Createmodeldept -Createmodelemp
得到的SQL如下:
>pythonmanage.pysqlmigratescott0001 BEGIN; CREATETABLE"dept"("deptno"integerNOTNULLPRIMARYKEYAUTOINCREMENT,"dname"varchar(30)NOTNULL,"loc"varchar(30)NOTNULL); CREATETABLE"emp"("empno"integerNOTNULLPRIMARYKEYAUTOINCREMENT,"ename"varchar(30)NOTNULL,"job"varchar(30)NOTNULL,"mgr"integerNOTNULL ,"hiredate"datetimeNOTNULL,"sal"integerNOTNULL,"deptno_id"integerNOTNULLREFERENCES"dept"("deptno")); CREATEINDEX"emp_d6b13549"ON"emp"("deptno_id"); COMMIT;
简单确认下,我们就可以生成创建出来这两个表了,使用python manage.py migrate即可。
emp的表结构如下:
dept的表结构如下:
我们初始化一下数据,这个时候直接使用SQL也可以.
dept表的初始化语句如下:
insertintodeptvalues(10,'ACCOUNTING','NEWYORK'); insertintodeptvalues(20,'RESEARCH','DALLAS'); insertintodeptvalues(30,'SALES','CHICAGO'); insertintodeptvalues(40,'OPERATIONS','BOSTON');
emp表的初始化语句如下,特别需要注意的就是字段不是deptno,而是deptno_id
insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7369,'SMITH','CLERK',7902,'1980-12-17',800.00,20); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7499,'ALLEN','SALESMAN',7698,'1981-2-20',1600.00,30); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7521,'WARD','SALESMAN',7698,'1981-2-22',1250.00,30); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7566,'JONES','MANAGER',7839,'1981-4-2',2975.00,20); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7654,'MARTIN','SALESMAN',7698,'1981-9-28',1250.00,30); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7698,'BLAKE','MANAGER',7839,'1981-5-1',2850.00,30); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7782,'CLARK','MANAGER',7839,'1981-6-9',2450.00,10); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7788,'SCOTT','ANALYST',7566,'1987--4-19',3000.00,20); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7839,'KING','PRESIDENT',0,'1981-11-17',5000.00,10); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7844,'TURNER','SALESMAN',7698,'1981-9-8',1500.00,30); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7876,'ADAMS','CLERK',7788,'1987-5-23',1100.00,20); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7900,'JAMES','CLERK',7698,'1981-12-3',950,30); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7902,'FORD','ANALYST',7566,'1981-12-3',3000,20); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(7934,'MILLER','CLERK',7782,'1982-1-23',1300,10); insertintoemp(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,DEPTNO_ID)values(9999,'SHUNPING','CLERK',7782,'1988-5-5',2456.34,10);
剩下的事情就是实践了。我们就选择emp,dept常见的一些SQL来看看ORM能否完成这个任务。
1、显示所有的姓名、工种、工资和奖金,按工种降序排列,若工种相同则按工资升序排列。
如果使用MySQL,语句和数据结果如下:
mysql>selectename,job,salfromemporderbyjobdesc,salasc; +----------+-----------+------+ |ename|job|sal| +----------+-----------+------+ |WARD|SALESMAN|1250| |MARTIN|SALESMAN|1250|
使用order_by的方式来处理,可以看到有了一点头绪,但是还是没有实现需求。
>>>emp.objects.all().order_by('job') [<emp:7788SCOTT>,<emp:7902FORD>,<emp:7369SMITH>,....
所以我们的重点就是排序了,ORM本身有order_by函数,还可以调整DESC,ASC,所以一个基本符合要求的方式如下:
>>>emp.objects.all().order_by(('-job'),('sal')) [<emp:7521WARD>,<emp:7654MARTIN>,<emp:7844TURNER>
第二个题目也是类似的。
2、查询员工的姓名和入职日期,并按入职日期从先到后进行排列。
SQL语句如下:
selectename,hiredatefromemporderbyhiredateasc;
现在的语句如下:
emp.objects.all().order_by(('hiredate'))
3. 计算工资***的员工
这个需求充分考虑到聚合函数的部分,我们可以使用aggregate来完成这个工作。
>>>emp.objects.all().aggregate(Max('sal')) {'sal__max':5000}
4.查询至少有一个员工的部门信息。
这个部分会涉及到表关联关系,如果是通过SQL的方式,语句如下:
select*fromdeptwheredeptnoin(selectdistinctdeptnofromempwheremgrisnotnull);
执行的结果如下,可以看到***种方式能出结果,但是还是存在重复值,需要用distinct过啦一下。
>>>dept.objects.filter(emp__mgr__isnull=False) [<dept:10ACCOUNTING>,<dept:10ACCOUNTING>,<dept:10ACCOUNTING>,<dept:10ACCOUNTING>,<dept:20RESEARCH>,<dept:20RESEARCH>,<dept:20RESEARCH>,<dept:20RESEARCH>,<dept:20RESEARCH>,<dept:30SALES>,<dept:30SALES>,<dept:30SALES>,<dept:30SALES>,<dept:30SALES>,<dept:30SALES>] >>>dept.objects.filter(emp__mgr__isnull=False).distinct() [<dept:10ACCOUNTING>,<dept:20RESEARCH>,<dept:30SALES>] >>>
后续继续补充ORM的内容。