聯合主鍵問題
SQLAlchemy提供了聯合主鍵支持,但是Django至今都沒有支持。
Django只支持單一主鍵,但對於本次基於Django測試的表就只能增加一個單一主鍵
了。
原因,請參看 https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys 。Django 到目前為
止也沒有提供這種Composite primary key
Django不能直接添加自己的2個字段的聯合主鍵,我們手動為表創建一個自增id主鍵。操作順序如下:
-
取消表所有聯合主鍵,並刪除所有外鍵約束后保存,成功再繼續
-
為表增加一個id字段,自增、主鍵。保存,如果成功,它會自動填充數據
-
重建原來的外鍵約束即可
具體操作如下:
models構建模型
class Salary(models.Model): class Meta: db_table = 'salaries' id = models.AutoField(primary_key=True) # 如果覺得如果覺得salary_set不好用,可以使用related_name ↓ print(emgr.get(pk=10004).salary_set.all()) 可替換為 print(emgr.get(pk=10004).salaries.all()) # emp_no = models.ForeignKey(to='Employee', on_delete=models.CASCADE, null=False, db_column='emp_no', related_name='salaries') emp_no = models.ForeignKey(to='Employee', on_delete=models.CASCADE, null=False, db_column='emp_no') salary = models.IntegerField(null=False) from_date = models.DateField(null=False) to_date = models.DateField(null=False) def __repr__(self): return "<Salary: {} {} {}>".format(self.emp_no_id, self.from_date, self.salary) __str__ = __repr__
特殊屬性
如果增加了外鍵約束后,Django會對一端和多端增加一些新的類屬性
print(*Employee.__dict__.items(), sep='\n') # 一端,Employee類中多了一個類屬性 # ('salary_set', <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x000001303FB09B38>) print(*Salary.__dict__.items(), sep='\n') # 多端,Salary類中也多了一個類屬性 # ('emp_no_id', <django.db.models.query_utils.DeferredAttribute object at 0x000001303FB09828>) # ('emp_no', <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x000001303FB09860>) 指向Employee類的一個實例
從一端往多端查 <Employee_instance>.salary_set
從多端往一端查 <Salary_instance>.emp_no
注意: 多出來的2個屬性,只有用到
類似get方法取到具體對象時候才能用,如果是
類似filter方法取到的集合對象不能用這2個屬性
查詢
一對多查詢
print(emgr.get(pk=10004).salary_set.all()) # 查詢工號為10004的員工的所有工資,因為有了salary_set的存在,所有可以直接在Employee表中完成查詢 print(smgr.filter(emp_no=10004)) # 直接利用關連鍵emp_no查找 print(emgr.get(pk=10004).salaries.all()) # 利用related_name關聯名字 print(emgr.get(pk=10004).salary_set.values('emp_no', 'from_date', 'salary')) # 投影(注意事項get方法,只有單個對象不是set集合才能用salary_set屬性) print(smgr.filter(salary__gt=50000).values('emp_no').annotate(c=Count('id')).values('emp_no', 'c')) # 查詢工資大於50000的員工 print(smgr.filter(salary__gt=50000).values('emp_no').annotate(c=Count('id')).values('emp_no').emp_no_id.values('name')) # 查詢工資大於50000的工資員工名稱 emp_nos = smgr.filter(salary__gt=50000).values('emp_no').distinct() print(emgr.filter(emp_no__in=[d.get('emp_no') for d in emp_nos]).values('first_name'))
如果覺得salary_set不好用,可以使用related_name
class Salary(models.Model): emp_no = models.ForeignKey('Employee', on_delete=models.CASCADE, null=False, db_column='emp_no', related_name='salaries') print(empmgr.get(pk=10004).salaries.all())
去重distinct
print(smgr.values('emp_no').distinct()) # 查詢所有有薪水的工號(直接投影過濾,然后利用distinct去重)
raw的使用(不太推薦)
如果查詢非常復雜,使用Django不方便,可以直接使用SQL語句
# 工資大於50000的所有員工的姓名 sql = """\ SELECT DISTINCT employees.emp_no,employees.first_name FROM employees Join salaries ON employees.emp_no=salaries.emp_no WHERE salaries.salary > 50000 """ emps = emgr.raw(sql) print(type(emps)) print(list(emps)) # 員工工資記錄里超過70000的人的工資和姓名 sql = """\ select e.emp_no, e.first_name, e.last_name, s.salary from employees e join salaries s on e.emp_no = s.emp_no where s.salary > 70000 """ s = emgr.raw(sql) for x in s: # print(x.__dict__) print(x.name, x.salary)
還有一些多對多的查詢,差不太多。