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', }, }, }