Django的ORM一對多查詢及聯合查詢


聯合主鍵問題

SQLAlchemy提供了聯合主鍵支持,但是Django至今都沒有支持。
Django只支持單一主鍵,但對於本次基於Django測試的表就只能增加一個單一主鍵
了。
 
原因,請參看 https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys 。Django 到目前為
止也沒有提供這種Composite primary key
Django不能直接添加自己的2個字段的聯合主鍵,我們手動為表創建一個自增id主鍵。操作順序如下:
 
  1. 取消表所有聯合主鍵,並刪除所有外鍵約束后保存,成功再繼續
  2. 為表增加一個id字段,自增、主鍵。保存,如果成功,它會自動填充數據
  3. 重建原來的外鍵約束即可

 

具體操作如下:

 

 

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)

 

還有一些多對多的查詢,差不太多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM