天天看點

Django ORM查詢總結

employee models

import os
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)


from employee.models import Employee, Salary, Department, Dept_emp
from students.models import Student, Score
from django.db.models import Q, Avg, Sum, Max, Min, Count

emps = Employee.objects.all()
smps = Salary.objects
dmps = Department.objects
demps = Dept_emp.objects

# --- 多對多查詢

# 查詢10010員工的所在的部門編号及員工資訊
# es = emps.get(pk=10010)
# ed = es.dept_emp_set.all() # 記得加all(),不然會傳回None
# print(es)
# print(ed)


# for i in ed:
    # print(type(i), i)
    # e = i.emp_no
    # print(type(e), e)
    # d = i.dept_no
    # print(type(d), d, d.dept_no, d.dept_name)

# --- 一對多查詢

# print(smps.all())
# print(smps.filter(pk__gt=30)) # 直接用Salary.objects即可管理
#
# print(*Employee.__dict__.items(), sep='\n')
# print('-'*30)
# print(*Salary.__dict__.items(), sep='\n')

# 查詢1004号員工的所有工資(建議從"一"端查,隻查詢一次)
# print(emps.get(pk=10004).salary_set.all()) # 記得加all()

# 工資大于55000的所有員工的姓名
# print(smps.filter(salary__gt=55000).values('emp_no').distinct())

# 查詢1004号員工的所有工資及姓名(建議從"一"端查,隻查詢一次)
# print(emps.get(pk=10004).salary_set.all()) # 記得加all()
# print(emps.get(pk=10004).name)

# 查詢工資大于55000的所有員工姓名(emp_no僅表示關系,salary_set,emp_no才是2個表真正用的屬性)
# nos = smps.filter(salary__gt=55000)
# names = set()
# for i in nos:
#     names.add(i.emp_no.name)
# print(names)
# 查詢10004員工所有工資及姓名
# slist = list(smps.filter(emp_no=10004).filter(salary__gt=55000))
# for s in slist:
#     print(s.emp_no.name, s.emp_no_id, s.salary) # s.emp_no 這裡的emp_no表示【關系】

# 員工大于55000的所有員工的姓名
#
# sql = """
# SELECT DISTINCT e.emp_no, e.first_name, e.last_name
# FROM employees e JOIN salaries s
# ON e.emp_no=s.emp_no
# WHERE s.salary > 55000
# """
# # print(emps.raw(sql)) #惰性的set
# print(list(emps.raw(sql)))


# --- 緩存驗證

# print(emps)
# print(*emps, sep='\n')
# print(emps[0].gender, emps[0].get_gender_display())

# print(type(emps))
# print(1, emps)
# print(2, emps)
# print(3, emps[0])
# print(4, emps[0])
# print(emps._result_cache)
# # 上面共查詢了4次資料庫,emps._result_cache為None,說明emps是惰性的

# print(type(emps)) # QuerySet查詢集
# print(list(emps)) # print(*emps) 先周遊一遍,緩存住
# print(1, emps)
# print(2, emps)
# print(3, emps[0])
# print(4, emps[:])
# print(emps._result_cache) # 結果集清單
# # 上面list(emps)進行了緩存,是以下面4個就不進行查詢了,直接利用了緩存,綜合查詢了1次

# # --- 切片和步長
# print(emps[10:15])
# print(emps[20:30])
# print(emps[0:20:5])
# print(emps[::5])

# # --- 結果集查詢
# print(emps.values())
# print(emps.filter(pk=10010).values())
# print(emps.exclude(emp_no=10001))
# print(emps.exclude(emp_no=10002).order_by('emp_no'))
# print(emps.exclude(emp_no=10002).order_by('-pk'))
# print(emps.exclude(emp_no=10002).order_by('-pk').values())
# # values傳回的集合裡的元素是字典

# # --- 單值查詢
# print(emps.filter(pk=10010).get())
# print(emps.get(pk=10001))
# #print(emps.exclude(pk=10010).get()) # get嚴格一個
# print(emps.first()) # limit 1
# print(emps.exclude(pk=10010).last()) # desc, limit 1
# print(emps.filter(pk=10010, gender=1).first()) # AND,找不到傳回None
# print(emps.count())
# print(emps.exclude(pk=10010).count())

# # --- LOOKUP表達式
# print(emps.filter(emp_no__exact=10010)) # 就是等于,是以很少用exact
# print(emps.filter(pk__in=[10010, 10009]))
# print(emps.filter(last_name__startswith='P'))
# print(emps.exclude(pk__gt=10003))

# # --- Q對象
# print(emps.filter(Q(pk__lt=10006))) # 不如直接寫filter(pk__lt=10006)
# # 下面幾句一樣
# print(emps.filter(pk__gt=10003).filter(pk__lt=10006)) # 與
# print(emps.filter(pk__gt=10003, pk__lt=10006)) # 與
# print(emps.filter(Q(pk__gt=10003), Q(pk__lt=10006)))
# print(emps.filter(Q(pk__gt=10003) & Q(pk__lt=10006))) # 與
# print(emps.filter(pk__gt=10003) & emps.filter(pk__lt=10006))
# # 下面幾句等價
# print(emps.filter(pk__in=[10003, 10006])) # in
# print(emps.filter(Q(pk=10003) | Q(pk=10006))) # 或
# print(emps.filter(pk=10003) | emps.filter(pk=10006))
#
# print(emps.filter(~Q(pk__gt=10003))) # 非
# # 可使用&|和Q對象來構造複雜的邏輯表達式,可以使用一個或多個Q對象。
# # 如果混用關鍵字參數和Q對象,那麼Q對象必須位于關鍵字參數的前面。

# --- 聚合分組

# # aggregate() 傳回字典,友善使用
# print(emps.filter(pk__gt=10010).count()) # 單值
# print(emps.filter(pk__gt=10010).aggregate(Count('pk'), Max('pk'))) # 字典
# print(emps.filter(pk__lte=10010).aggregate(Avg('pk')))
# print(emps.aggregate(Max('pk'), min=Min('pk'))) # 别名

# # annotate()方法用來分組聚合,傳回查詢集。
# print(emps.filter(pk__gt=10010).aggregate(Count('pk'))) # 字典
# s = emps.filter(pk__gt=10010).annotate(Count('pk')) # 傳回查詢集,沒指定分組字段,
# # print(s)
# # 使用主鍵分組
# for x in s:
#     print(x)
#     print(x.__dict__) # 裡面多了一個屬性pk__count

# # values()方法,放在annotate前就是指定分組字段,之後就是取結果中的字段。
# s = emps.filter(pk__gt=10010).values('gender').annotate(Count('pk')) # 查詢集
# print(s)
# for x in s:
#     print(x) # 字典

# s = emps.filter(pk__gt=10010).values('gender').annotate(c=Count('pk')).order_by('-c') # 查詢集
# print(s)
# for x in s:
#     print(x) # 字典

# s = emps.filter(pk__gt=10010).values('gender').annotate(Avg('pk'), c=Count('pk')).order_by('-c').values('pk__avg', 'c') # 查詢集,但後面的values過濾了每個對象字典的key
# print(s)
# for x in s:
#     print(x) # 字典



# --- 練習題(記得用test2資料庫)

# # # 導入資料
#
# stu_list = [('王一涵',10),('張青陽',12),('韓名博',12),('王梓',13),('駱銘峰',11),('赢乘風',11),('林烽',10),('吳博文',12),('馬小文',12)]
# sco_list = [(1,'國文',90,'王一涵'),(2,'數學',80,'王一涵'),(3,'英語',75,'王一涵'),(4,'國文',95,'張青陽'),(5,'數學',90,'張青陽'),(6,'英語',98,'張青陽'),(7,'國文',80,'韓名博'),(8,'數學',89,'韓名博'),(9,'英語',70,'韓名博'),(10,'國文',60,'王梓'),(11,'數學',75,'王梓'),(12,'英語',65,'王梓'),(13,'國文',81,'駱銘峰'),(14,'數學',82,'駱銘峰'),(15,'英語',55,'駱銘峰'),(16,'國文',78,'赢乘風'),(17,'數學',89,'赢乘風'),(18,'英語',65,'赢乘風'),(19,'國文',89,'林烽'),(20,'數學',60,'林烽'),(21,'英語',49,'林烽'),(22,'國文',89,'吳博文'),(23,'數學',92,'吳博文'),(24,'英語',79,'吳博文'),(25,'國文',50,'馬小文'),(26,'數學',60,'馬小文'),(27,'英語',62,'馬小文')]
#
# for k,v in stu_list:
#     Student(k,v).save()
#
# for s1,s2,s3,s4 in sco_list:
#     Score(s1,s2,s3,s4).save()
#
#
# # 查詢
#
# stu = Student.objects.all()
# sco = Score.objects.all()
#
# # 總成績大于250分的學生資訊
# print(sco.values('name').annotate(Sum('score')).filter(score__sum__gt=250))
# # 國文成績在80-90分之間的學生資訊
# print(sco.filter(exam_subjects='國文').filter(score__gt=80,score__lt=90))
# # 有一門科目低于60分的學生資訊
# print(sco.filter(score__lt=60).values('name').annotate())
# # 平均分70以上的學生資訊
# print(sco.values('name').annotate(Avg('score')).filter(score__avg__gt=70))
# # 三門成績都大于90分的學生姓名
# print(sco.filter(score__gte=90).values('name').annotate(Count('score')).filter(score__count=3))      

students models

from django.db import models

# Create your models here.

class Student(models.Model):
    class Mate:
        db_table = 'students'

    # id = models.AutoField(primary_key=True)
    name = models.CharField(primary_key=True, max_length=30, verbose_name='姓名')
    age = models.PositiveSmallIntegerField(verbose_name='年齡')

    def __repr__(self):
        return '{} {}'.format(self.name, self.age)

    __str__ = __repr__

class Score(models.Model):
    class Mate:
        db_table = 'scroes'

    id = models.AutoField(primary_key=True)
    exam_subjects = models.CharField(max_length=40, verbose_name='科目')
    score = models.PositiveSmallIntegerField(verbose_name='分數')
    name = models.ForeignKey(Student, db_column='name', on_delete=models.CASCADE)
    # to_field = name (預設用的Studentde主鍵)
    # name表示關系,db_column='name'的name表示資料庫顯示的名稱(資料庫用的名稱),ORM真正與資料的name對應的卻是name_id

    def __repr__(self):
        return '{} {} {}'.format(self.exam_subjects, self.score, self.name_id)

    __str__ = __repr__      

test.py

import os
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)


from employee.models import Employee, Salary, Department, Dept_emp
from students.models import Student, Score
from django.db.models import Q, Avg, Sum, Max, Min, Count

emps = Employee.objects.all()
smps = Salary.objects
dmps = Department.objects
demps = Dept_emp.objects

# --- 多對多查詢

# 查詢10010員工的所在的部門編号及員工資訊
# es = emps.get(pk=10010)
# ed = es.dept_emp_set.all() # 記得加all(),不然會傳回None
# print(es)
# print(ed)


# for i in ed:
    # print(type(i), i)
    # e = i.emp_no
    # print(type(e), e)
    # d = i.dept_no
    # print(type(d), d, d.dept_no, d.dept_name)

# --- 一對多查詢

# print(smps.all())
# print(smps.filter(pk__gt=30)) # 直接用Salary.objects即可管理
#
# print(*Employee.__dict__.items(), sep='\n')
# print('-'*30)
# print(*Salary.__dict__.items(), sep='\n')

# 查詢1004号員工的所有工資(建議從"一"端查,隻查詢一次)
# print(emps.get(pk=10004).salary_set.all()) # 記得加all()

# 工資大于55000的所有員工的姓名
# print(smps.filter(salary__gt=55000).values('emp_no').distinct())

# 查詢1004号員工的所有工資及姓名(建議從"一"端查,隻查詢一次)
# print(emps.get(pk=10004).salary_set.all()) # 記得加all()
# print(emps.get(pk=10004).name)

# 查詢工資大于55000的所有員工姓名(emp_no僅表示關系,salary_set,emp_no才是2個表真正用的屬性)
# nos = smps.filter(salary__gt=55000)
# names = set()
# for i in nos:
#     names.add(i.emp_no.name)
# print(names)
# 查詢10004員工所有工資及姓名
# slist = list(smps.filter(emp_no=10004).filter(salary__gt=55000))
# for s in slist:
#     print(s.emp_no.name, s.emp_no_id, s.salary) # s.emp_no 這裡的emp_no表示【關系】

# 員工大于55000的所有員工的姓名
#
# sql = """
# SELECT DISTINCT e.emp_no, e.first_name, e.last_name
# FROM employees e JOIN salaries s
# ON e.emp_no=s.emp_no
# WHERE s.salary > 55000
# """
# # print(emps.raw(sql)) #惰性的set
# print(list(emps.raw(sql)))


# --- 緩存驗證

# print(emps)
# print(*emps, sep='\n')
# print(emps[0].gender, emps[0].get_gender_display())

# print(type(emps))
# print(1, emps)
# print(2, emps)
# print(3, emps[0])
# print(4, emps[0])
# print(emps._result_cache)
# # 上面共查詢了4次資料庫,emps._result_cache為None,說明emps是惰性的

# print(type(emps)) # QuerySet查詢集
# print(list(emps)) # print(*emps) 先周遊一遍,緩存住
# print(1, emps)
# print(2, emps)
# print(3, emps[0])
# print(4, emps[:])
# print(emps._result_cache) # 結果集清單
# # 上面list(emps)進行了緩存,是以下面4個就不進行查詢了,直接利用了緩存,綜合查詢了1次

# # --- 切片和步長
# print(emps[10:15])
# print(emps[20:30])
# print(emps[0:20:5])
# print(emps[::5])

# # --- 結果集查詢
# print(emps.values())
# print(emps.filter(pk=10010).values())
# print(emps.exclude(emp_no=10001))
# print(emps.exclude(emp_no=10002).order_by('emp_no'))
# print(emps.exclude(emp_no=10002).order_by('-pk'))
# print(emps.exclude(emp_no=10002).order_by('-pk').values())
# # values傳回的集合裡的元素是字典

# # --- 單值查詢
# print(emps.filter(pk=10010).get())
# print(emps.get(pk=10001))
# #print(emps.exclude(pk=10010).get()) # get嚴格一個
# print(emps.first()) # limit 1
# print(emps.exclude(pk=10010).last()) # desc, limit 1
# print(emps.filter(pk=10010, gender=1).first()) # AND,找不到傳回None
# print(emps.count())
# print(emps.exclude(pk=10010).count())

# # --- LOOKUP表達式
# print(emps.filter(emp_no__exact=10010)) # 就是等于,是以很少用exact
# print(emps.filter(pk__in=[10010, 10009]))
# print(emps.filter(last_name__startswith='P'))
# print(emps.exclude(pk__gt=10003))

# # --- Q對象
# print(emps.filter(Q(pk__lt=10006))) # 不如直接寫filter(pk__lt=10006)
# # 下面幾句一樣
# print(emps.filter(pk__gt=10003).filter(pk__lt=10006)) # 與
# print(emps.filter(pk__gt=10003, pk__lt=10006)) # 與
# print(emps.filter(Q(pk__gt=10003), Q(pk__lt=10006)))
# print(emps.filter(Q(pk__gt=10003) & Q(pk__lt=10006))) # 與
# print(emps.filter(pk__gt=10003) & emps.filter(pk__lt=10006))
# # 下面幾句等價
# print(emps.filter(pk__in=[10003, 10006])) # in
# print(emps.filter(Q(pk=10003) | Q(pk=10006))) # 或
# print(emps.filter(pk=10003) | emps.filter(pk=10006))
#
# print(emps.filter(~Q(pk__gt=10003))) # 非
# # 可使用&|和Q對象來構造複雜的邏輯表達式,可以使用一個或多個Q對象。
# # 如果混用關鍵字參數和Q對象,那麼Q對象必須位于關鍵字參數的前面。

# --- 聚合分組

# # aggregate() 傳回字典,友善使用
# print(emps.filter(pk__gt=10010).count()) # 單值
# print(emps.filter(pk__gt=10010).aggregate(Count('pk'), Max('pk'))) # 字典
# print(emps.filter(pk__lte=10010).aggregate(Avg('pk')))
# print(emps.aggregate(Max('pk'), min=Min('pk'))) # 别名

# # annotate()方法用來分組聚合,傳回查詢集。
# print(emps.filter(pk__gt=10010).aggregate(Count('pk'))) # 字典
# s = emps.filter(pk__gt=10010).annotate(Count('pk')) # 傳回查詢集,沒指定分組字段,
# # print(s)
# # 使用主鍵分組
# for x in s:
#     print(x)
#     print(x.__dict__) # 裡面多了一個屬性pk__count

# # values()方法,放在annotate前就是指定分組字段,之後就是取結果中的字段。
# s = emps.filter(pk__gt=10010).values('gender').annotate(Count('pk')) # 查詢集
# print(s)
# for x in s:
#     print(x) # 字典

# s = emps.filter(pk__gt=10010).values('gender').annotate(c=Count('pk')).order_by('-c') # 查詢集
# print(s)
# for x in s:
#     print(x) # 字典

# s = emps.filter(pk__gt=10010).values('gender').annotate(Avg('pk'), c=Count('pk')).order_by('-c').values('pk__avg', 'c') # 查詢集,但後面的values過濾了每個對象字典的key
# print(s)
# for x in s:
#     print(x) # 字典



# --- 練習題(記得用test2資料庫)

# # # 導入資料
#
# stu_list = [('王一涵',10),('張青陽',12),('韓名博',12),('王梓',13),('駱銘峰',11),('赢乘風',11),('林烽',10),('吳博文',12),('馬小文',12)]
# sco_list = [(1,'國文',90,'王一涵'),(2,'數學',80,'王一涵'),(3,'英語',75,'王一涵'),(4,'國文',95,'張青陽'),(5,'數學',90,'張青陽'),(6,'英語',98,'張青陽'),(7,'國文',80,'韓名博'),(8,'數學',89,'韓名博'),(9,'英語',70,'韓名博'),(10,'國文',60,'王梓'),(11,'數學',75,'王梓'),(12,'英語',65,'王梓'),(13,'國文',81,'駱銘峰'),(14,'數學',82,'駱銘峰'),(15,'英語',55,'駱銘峰'),(16,'國文',78,'赢乘風'),(17,'數學',89,'赢乘風'),(18,'英語',65,'赢乘風'),(19,'國文',89,'林烽'),(20,'數學',60,'林烽'),(21,'英語',49,'林烽'),(22,'國文',89,'吳博文'),(23,'數學',92,'吳博文'),(24,'英語',79,'吳博文'),(25,'國文',50,'馬小文'),(26,'數學',60,'馬小文'),(27,'英語',62,'馬小文')]
#
# for k,v in stu_list:
#     Student(k,v).save()
#
# for s1,s2,s3,s4 in sco_list:
#     Score(s1,s2,s3,s4).save()
#
#
# # 查詢
#
# stu = Student.objects.all()
# sco = Score.objects.all()
#
# # 總成績大于250分的學生資訊
# print(sco.values('name').annotate(Sum('score')).filter(score__sum__gt=250))
# # 國文成績在80-90分之間的學生資訊
# print(sco.filter(exam_subjects='國文').filter(score__gt=80,score__lt=90))
# # 有一門科目低于60分的學生資訊
# print(sco.filter(score__lt=60).values('name').annotate())
# # 平均分70以上的學生資訊
# print(sco.values('name').annotate(Avg('score')).filter(score__avg__gt=70))
# # 三門成績都大于90分的學生姓名
# print(sco.filter(score__gte=90).values('name').annotate(Count('score')).filter(score__count=3))      

settings.py

"""
Django settings for salary project.

Generated by 'django-admin startproject' using Django 3.2.6.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-(uouz1h2%5ey)2+n6o8efxd+p+h!@m&we+1%6erp6ng79j9p#2'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'employee',
    'students',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'salary.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'salary.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {'charset': 'utf8'},
        'NAME': 'test',
        'USER': 'soymilk',
        'PASSWORD': '123456',
        'HOST': '172.16.241.2',
        'PORT': '3306',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'zh-Hans' # 'en-us'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },

    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}