Django的单表操作

单表

  1. model.py中创建模型

class Employee(models.Model):
    id = models.AutoField(primary_key=True)  # 不写自动创建
    name = models.CharField(max_length=16)
    gender = models.BooleanField(default=1)  # 0女,1男
    birth = models.DateField(auto_now_add=True)
    department = models.CharField(max_length=32)
    salary = models.DecimalField(max_digits=10, decimal_places=1)

  def __str__(self):
    return self.name

  2. django连接mysql

  settings.py文件

# 删除\注释掉原来的DATABASES配置项,新增下述配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # 使用mysql数据库
        'NAME': 'db1',          # 要连接的数据库
        'USER': 'root',         # 链接数据库的用于名
        'PASSWORD': '',         # 链接数据库的用于名                  
        'HOST': '127.0.0.1',    # mysql服务监听的ip  
        'PORT': 3306,           # mysql服务监听的端口  
        'ATOMIC_REQUEST': True, #设置为True代表同一个http请求所对应的所有sql都放在一个事务中执行 
                                #(要么所有都成功,要么所有都失败),这是全局性的配置,如果要对某个
                                #http请求放水(然后自定义事务),可以用non_atomic_requests修饰器 
        'OPTIONS': {
            "init_command": "SET storage_engine=INNODB", #设置创建表的存储引擎为INNODB
        }
    }
}

  3. 连接数据库之前首先创建好数据库

mysql> create database db1;

  4. pymysql连接数据库mysql

   django的orm底层操作数据库的python模块默认是mysqldb而非pymysql,

  然而对于解释器而言,python2.x解释器支持的操作数据库的模块是mysqldb,

  而python3.x解释器支持的操作数据库的模块则是pymysql

  5. 执行数据库迁移命令, 会在指定的数据库中db1创建表

python manage.py makemigrations
python manage.py migrate

  注意:

    1. makemigrations只是生成一个数据库迁移记录的文件, migrate才是将数据提交到数据库中执行

    2. 数据库迁移文件记录在app01下的migrations文件夹中

  5.1 在django中执行表的迁移命令的时候报错: 某个表已经存在

解决方法: 在Django中contrib/admin找到migrations.py文件删除里面的表迁移记录即可

  6.想打印orm转换过程中的SQL, 需要在settings.py文件中配置日志

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

  7. 在表生成后, 如果需要添加, 删除, 修改表中字段

# 1. 增加字段
在模型类Employee里直接新增字段,强调:对于orm来说,新增的字段必须用default指定默认值
如: publish = models.CharField(max_length=12,default='人民出版社',null=True)
重新执行那两条数据库迁移命令

# 2. 删除字段
 直接注释掉字段
 重新执行那两条数据库迁移命令

# 3. 修改字段
将模型类中字段修改
重新执行那两条数据库迁移命令

 增删改查

  1. 增

  方式一

# 1、用模型类创建一个对象,一个对象对应数据库表中的一条记录
obj = Employee(name="zsh", gender=0, birth='1997-01-27', department="开发部", salary=100.1)
# 2、调用对象下的save方法,即可以将一条记录插入数据库
obj.save()

  方式二

# 每个模型表下都有一个objects管理器,用于对该表中的记录进行增删改查操作,其中增加操作如下所示
obj = Employee.objects.create(name="zsh", gender=0, birth='1997-01-27', department="开发部", salary=100.1)

# 对于注册的用户使用create_user, 用来对password进行加密
obj = Employee.objects.create_user(name="zsh", gender=0, birth='1997-01-27', department="开发部", salary=100.1, password="123")

  2. 查

  2.1 基础的查询

 get, first, last
 count, exists
filter, exclude, all, order_by, values, values_list, reverse, distinct

  返回的为Employee的对象(除count之外)

# get(**kwargs)
obj = Employee.objects.get(id=1)
print(obj.name, obj.salary)

# first() 查询的第一个结果
obj = Employee.objects.first()
print(obj.name)
# last 返回查询的最后一个对象
obj = models.Book.objects.last()
# count() 对查询的数据进行计数, 结果是数字
res = models.Book.objects.count()

  返回的为QuerySet对象, 存放查询出来的多个对象

# filter(**kwargs)  QuerySet对象中包含了符合过滤条件的多个记录对象
queryset_res=Employee.objects.filter(department='技术部')

# exclude(**kwargs)
# QuerySet对象中包含了不符合过滤条件的多个记录对象
queryset_res=Employee.objects.exclude(department='技术部')

# all()
# QuerySet对象中包含了查询出的所有记录对象
queryset_res = Employee.objects.all() # 查询出表中所有的记录对象

# order_by(*field)
# 参数为排序字段,可以指定多个字段,在字段1相同的情况下,可以按照字段2进行排序
queryset_res = Employee.objects.order_by("salary","-id") # 先按照salary字段升序排,如果salary相同则按照id字段降序排

# values(*field)
# 参数为字段名,可以指定多个字段. QuerySet对象中包含的并不是一个个的记录对象,而上多个字典,字典的key即我们传入的字段名
queryset_res = Employee.objects.values('id','name')
# 结果<QuerySet [{'id': 1, 'name': 'zsh'}, {'id': 2, 'name': 'Bon'}, ......]>
print(queryset_res[0]['name'])

# values_list(*field)
# 参数为字段名,可以指定多个字段.QuerySet对象中包含的并不是一个个的记录对象,而上多个小元组,字典的key即我们传入的字段名
queryset_res = Employee.objects.values_list('id','name')
# 结果: <QuerySet [(1, 'zsh'), (2, 'Bon'),), ......]>
print(queryset_res[0][1])

# reverse()
# 对排序的结果取反,返回值为QuerySet对象
queryset_res = Employee.objects.order_by("salary", "-id").reverse()

# exists()
# 返回值为布尔值,如果QuerySet包含数据,就返回True,否则返回False
res = Employee.objects.filter(id=100).exists()

# distinct()
# 从values或values_list的返回结果中剔除重复的记录对象,返回值为QuerySet对象
res = Employee.objects.filter(name='Egon').values('name', 'salary').distinct()

  django的ORM支持链式操作

res=Employee.objects.filter(gender=1).order_by('-id').values_list('id','name')
print(res) # 输出:<QuerySet [(6, 'zsh'), (5, 'Bon'), (4, 'Tom'), (2, 'hg')]>

  2.2 基于双下划线的模糊查询

# id__in=[1,3,5]  包含某个值, 查询id为1,3,5的
Employee.objects.filter(id__in=[1,3,5])

# id__gt=3  查询id大于3的
Employee.objects.filter(id__gt=3)

# id__lt=3 查询id小于3的
Employee.objects.filter(id__lt=3)

# id__gte=3 查询id大于等于3的
Employee.objects.filter(id__gte=3)

# id__lte=3 查询id小于等于3的
Employee.objects.filter(id__lte=3)

# id__range=[1,3] 查新id1到3的
Employee.objects.filter(id__range=[1,3])

# name__contains="z" 查询name中有z的, 区别大小写
Employee.objects.filter(name__contains="z")

# name__icontains="z" 查询name中含有z, 不区别大小写
Employee.objects.filter(name__icontains="z")

# name__startswith="z" 查询name是以'z'开头的, 区别大小写
Employee.objects.filter(name__startswith="z")

# name__istartswith="z" 查询name是以'z'开头的, 不区别大小写
Employee.objects.filter(name__istartswith="z")

# date 可以根据年月日进行过滤
Employee.objects.filter(birth__year=1995)  # 查询出生在1995年的
Employee.objects.filter(birth__month=2)  # 查询出生在2月的

  2.3 F与Q查询

   2.3.1 F查询

  我们在进行条件过滤时, 都只是用某个字段与某个具体的值做比较

  如果我们要对两个字段的值做比较, 该如何比较?

  Django提供F()来做比较. F()的实列可以在查询中引用字段, 来比较两个不同字段的值

# 一张书籍表中包含字段:评论数commentNum、收藏数keepNum,要求查询:评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))

  Django支持F()对象之间以及F()对象的常数之间的加减乘除和取模的操作

# 查询评论数大于收藏数2倍的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum')*2)

  修改操作也可以使用F函数, 比如将每一本书的价格提高20元

Book.objects.all().update(price=F("price")+30)

  更新字段等于字段本身在拼接一个字符串应该这样做

from djiango.db.models.functions import Concat
from django.db.models import Value


Employee.objects.filter(nid__lte=3).update(name=Concat(F('name'), Value('_sb)))

  2.3.2 Q查询

  filter()等方法中逗号隔开的多个关键字参数都是逻辑与的关系.

  如果我们需要使用逻辑或(OR)来连接多个条件,就用到了Django的Q对象

  可以将条件传给类Q实例化出一个对象, Q的对象可以使用&和|操作符组合起来

  &等同于and, |等同于or

form django.db.models import Q

# 查找id大于5或者name='zsh'的
Employee.objects.filter(Q(id__gt=5) | Q(name="zsh"))

  Q对象可以使用~操作符取反, 相等于not

from django.db.models import Q

Employee.objects.filter(~Q(id__gt=5) | Q(name='zsh'))

  当我们的过滤条件中既有or又有and, 则需要混合使用Q对象与关键字参数,

  单Q对象必须位于所有关键字参数的前面

from django.db.modes import Q


Employee.objects.filter(Q(id__gt=5) | Q(name='zsh'), salary__lt=100)

# 等同于sql
select * from app01_employee where (id>5 or name='zsh') and salary<100;

  2.4 聚合查询

   聚合查询aggregate()是把所有查询出的记录对象整体当做一个组,

  我们可以搭配聚合函数来对整体进行一个聚合操作

from django.db.models import Avg, Max, Sum, Min, Count


# 1. 调用objects下的aggregate()方法, 会把表中所有记录对象当做一组进行聚合
res = Employee.objects.aggregate(Avg("salary"))
# select avg(salary) as salary_avg from app01_employee;

# 2. aggregate()会把QuerySet对象中包含的所有记录对象当成一组进行聚合
res2=Employee.objects.all().aggregate(Avg("salary")) 
# select avg(salary) as salary__avg from app01_employee;

res3=Employee.objects.filter(id__gt=3).aggregate(Avg("salary")) 
# select avg(salary) as salary__avg from app01_employee where id > 3;
print(res3) # 输出:{'salary__avg': 71.0}

  aggregate()的返回值为字典类型, 字典的key是有"聚合字段的名称__聚合函数的名称"和成的

Avg("salary") 合成的名字为 'salary__avg'

  若我们想定制字典的key名, 我们可以指定关键参数, 如下:

res1=Employee.objects.all().aggregate(avg_sal=Avg('salary')) 
# select avg(salary) as avg_sal from app01_employee;

print(res1) # 输出:{'avg_sal': 70.73} # 关键字参数名就会被当做字典的key

  如果我们想要得到多个聚合结果, 那就需要为aggregate传入多个参数

res1=Employee.objects.all().aggregate(nums=Count('id'),avg_sal=Avg('salary'),max_sal=Max('salary')) 
# 相当于SQL:select count(id) as nums,avg(salary) as avg_sal,max(salary) as max_sal from app01_employee;

print(res1) # 输出:{'nums': 10, 'avg_sal': 70.73, 'max_sal': Decimal('200.3')}

  2.5 分组查询

   分组查询annotate()相当于SQL语句中的group_by, 是在分组后, 对每个组进行单独的聚合,

  需要强调的是, 在进行单表查询时, annotate()必须搭建values()使用: values("分组字段").annotate(聚合函数), 如下:

# 查询每个部门下的员工数
res = Employee.objects.values('department').annotate(num=Count('id'))
# 相当于SQL:
# select department, count(id) as num from app01_employee group_by department;

print(res)
# 结果: <QuerySet [{'department': '财务部', 'num': 2}, {'department': '技术部', 'num': 3}, {'department': '运营部', 'num': 2}]

  跟在annotate前的values方法是用来指定分组字段, 及group_by后的字段,

  而跟在annotate后的values方法,则是用来指定分组后要查询的字段,即select 后跟的字段

res=Employee.objects.values('department').annotate(num=Count('id')).values('num')
# 相当于sql:
# select count(id) as num from app01_employee group by department;

print(res)
# 输出:<QuerySet [{'num': 2}, {'num': 3}, {'num': 2}]>

  跟在annotate前的filter方法表示where条件,跟在annotate后的filter方法表示having条件,如下

# 查询男员工数超过2人的部门名
res=Employee.objects.filter(gender=1).values('department').annotate(male_count=Count("id")).filter(male_count__gt=2).values('department')

print(res) # 输出:<QuerySet [{'department': '技术部'}]>

# 解析:
# 1、跟在annotate前的filter(gender=1) 相当于 where gender = 1,先过滤出所有男员工信息
# 2、values('department').annotate(male_count=Count("id")) 相当于group by department,对过滤出的男员工按照部门分组,然后聚合出每个部门内的男员工数赋值给字段male_count
# 3、跟在annotate后的filter(male_count__gt=2) 相当于 having male_count > 2,会过滤出男员工数超过2人的部门
# 4、最后的values('department')代表从最终的结果中只取部门名

  总结:

1、values()在annotate()前表示group by的字段,在后表示取值
1、filter()在annotate()前表示where条件,在后表示having

  注意: 

  如果我们在annotate前没有指定values(),那默认用表中的id字段作为分组依据,而id各不相同,如此分组是没有意义的,如下

res=Employee.objects.annotate(Count('name')) # 每条记录都是一个分组
res=Employee.objects.all().annotate(Count('name')) # 同上

  3. 修改记录

  3.1 修改单条记录

# 1、获取记录对象
obj=Employee.objects.filter(name='Egon').first()
# 2、修改记录对象属性的值
obj.name='zsh'
obj.gender=1
# 3、重新保存
obj.save()

  3.2 修改QuerySet中的所有记录对象

queryset_obj=Employee.objects.filter(id__gt=5)
rows=queryset_obj.update(name='EGON',gender=1)

  4. 删除记录

  4.1 直接删除单条记录

obj=Employee.objects.first()
obj.delete()

  4.2 删除QuerySet中的所有对象

queryset_obj=Employee.objects.filter(id__gt=5)
rows=queryset_obj.delete()

  强调: 在工作中并不会真正的删除一条数据, 只是修改一种的一个字段

管理objects下并没有delete方法,这是一种保护机制,是为了避免意外地调用 Employee.objects.delete() 方法导致所有的记录被误删除从而跑路。

但如果你确认要删除所有的记录,那么你必须显式地调用管理器下的all方法,拿到一个QuerySet对象后才能调用delete方法删除所有

Employee.objects.all().delete()

 

猜你喜欢

转载自www.cnblogs.com/zhuangshenhao/p/12095843.html