06-模型层1——单表操作

简介

MTV框架包含一个重要的部分就是ORM————对象关系映射(Object Relational Mapping),它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动。
ORM是“对象-关系-映射”的简称(Object Relational Mapping)。
需要注意的是:

数据库必须提前创建好————ORM在数据库中进行表的操作。

ORM只能对表进行操作,也可以对表下面的记录进行操作,但是ORM不能对数据库进行操作,不能创建与删除数据库。

类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎,明白orm是什么了,剩下的就是怎么使用orm,怎么来写类对象关系语句。

还有一个sqlalchemy和他很像,但是django的orm没有独立出来让别人去使用,虽然功能比sqlalchemy更强大,但是别人用不了。

原生SQL与Django中的ORM对比:

sql中的表操作

#创建表:
CREATE TABLE employee(                                     
    id INT PRIMARY KEY auto_increment ,                    
    name VARCHAR (20),                                      
    gender BIT default 1,                                  
    birthday DATA ,                                         
    department VARCHAR (20),                                
    salary DECIMAL (8,2) unsigned,                          
  );                                                

#添加一条表纪录:                                                          
  INSERT employee (name,gender,birthday,salary,department)            
  VALUES   
  ("whw",1,"1992-12-12",8000,"IT");               

#查询一条表纪录:                                                           
SELECT * FROM employee WHERE age=24;                               

#更新一条表纪录:                                                           
UPDATE employee SET birthday="1991-10-24" WHERE id=1;              

#删除一条表纪录:                                                          
DELETE FROM employee WHERE name="whw";   

Django的ORM操作

#创建表
class Employee(models.Model):
     id=models.AutoField(primary_key=True)
     name=models.CharField(max_length=32)
     gender=models.BooleanField()
     birthday=models.DateField()
     department=models.CharField(max_length=32)
     salary=models.DecimalField(max_digits=8,decimal_places=2)

#添加一条表纪录:
emp=Employee(name="whw",gender=True,birthday="1991-12-12",epartment="IT")
emp.save()

#查询一条表纪录:
Employee.objects.filter(age=24)

#更新一条表纪录:
Employee.objects.filter(id=1).update(birthday="1991-10-24")

#删除一条表纪录:
Employee.objects.filter(name="whw").delete()

单表操作

创建表的过程

先创建一个数据库 whw

create database whw;

创建Django项目及相关配置

创建Django项目

然后创建一个Django项目 whw_dj3_ORM,然后在这个项目中创建一个app名为book:

python manage.py startapp book

相关的配置

(1)确保配置文件中的INSTALLED_APPS中写入我们常见的book应用的名称:
#在全局的settings.py的INSTALLED_APPS中加入book:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
#前面是既有的,这个book是新加的
'book',
]
(2)然后在book应用中的models.py文件中加入下面代码(如果没有名字必须命名为models)
from django.db import models
# Create your models here.
#注意这个Book类必须继承models.Model
class Book(models.Model):
    #AutoField为自增对象,括号里面的是限定条件
    id = models.AutoField(primary_key=True)
    #CharField为一个字符串,括号里面表示它的最大长度
    title = models.CharField(max_length=32)
    state = models.BooleanField()
    #DateField是存日期的
    pub_date =models.DateField()
    #DecimalField是一个浮点型:max_digits为最大的位数,但是有两位是小数——111111.11
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish = models.CharField(max_length=32)
    
    #打印这个类展示的是它的title
    def __str__(self):
        return self.title
#(3)更多字段与更多参数详见pythonbook~
(3)想要将模型转换为mysql数据库中的表,需要在全局的settings中配置:

需要注意的是在Navicat中创建数据库的时候这些参数都有设置的,而且下面的参数是连接本机的数据库
如果要连接远程数据库就需要远程数据库的ip 端口等信息了。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',#引擎,选mysql
        'NAME':'day58',#要连接的数据库,连接前需要创建好
        'USER':'root',#连接数据库的用户名
        'PASSWORD':'123',#连接数据库的密码
        'HOST':'127.0.0.1',#连接主机,默认本本机
        'PORT':3306,#端口 默认3306
        #Django中设置数据库的严格模式
        'OPTIONS':{
            'init_command':"set sql_mode='STRICT_TRANS_TABLES' ",
            }
        }
    }
(4)如果此时直接启动项目会报错:no module named MySQLdb。这是因为django默认你导入的驱动是MySQLdb.

可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL所以,我们只需要找到全局项目名文件下的init.py,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()
(5)还需要注意的一点是,如果我们用的是django2.0版本,然后python用的是3.4以上的版本,需要进行如下的操作:
A、在python与django的安装路径找到:C:\Users\dell\AppData\Local\Programs\Python\Python36\Lib\site-packages\django\db\backends\mysql
B、将里面的base.py文件中的
            if version < (1, 3, 3):
                raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)
这句话注释掉!!!
(6)最后通过两条数据库迁移命令即可在指定的数据库中创建表(在Terminal中敲这两行代码就行了):
python manage.py makemigrations
python manage.py migrate
(7)然后我们就可以在数据库中看到新建的表book_book(还有其他的表)了。
#注意其他的表其实是django的全局的那个settings文件中INSTALLED_APPS列表下面默认应用对应的表~如果不需要可以把它们注释掉(除了contenttypes)!

#因为contenttypes生成的表记录着哪个app建了什么表!

#把其他的应用注释掉的话~就不会生成那个应用对应的表了!注意注释contenttypes的话迁移的时候会报错!

#在创建好表后,如果遇到问题~想要“重新建表”~删掉单独的那个业务有关表没用,另外要把migrations文件夹中的0001_initial.py那个py文件删掉的话~注意还需要把contenttypes这个表里面的0001_initial的对应的数据条目删掉~——理解django在执行makemigrations与migrate的过程!

#创建好表后如果想修改表结构或者添加字段,执行完数据库迁移指令后会在migrations文件夹中生成0002_initial文件
(8-1)如果想打印orm转换过程中的sql,需要在settings中进行如下配置:
LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console':{
                'level':'DEBUG',
                'class':'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'propagate': True,
                'level':'DEBUG',
            },
        }
    }
(8-2)还有另外一种查看SQL语句的方式
from app01 import models

def add_book(request):
    '''
    添加表记录
    :param request: http请求信息
    :return:
    '''
    book_obj = models.Book(title='python',price=123,pub_date='2012-12-12',publish='人民出版社')
    book_obj.save()
    from django.db import connection  #通过这种方式也能查看执行的sql语句
    print(connection.queries)
    return HttpResponse('ok')

数据的增删改查

1、准备工作

1.1 所有的操作都在视图函数中进行,所以现在全局的urls中加入index的路由:
from django.contrib import admin
from django.urls import path,re_path
from book import views

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^index$',views.index)
]
1.2 应用book的views视图函数是这样的:
from django.shortcuts import render,HttpResponse
#注意这里需要引入models!
from book import models
# Create your views here.
def index(request):
    #这里写函数的主体
    return HttpResponse('OK')

以后所有的操作都在book应用的views.py文件下的index函数中进行进行

2、添加表记录

create方法的返回值book_obj就是插入book表中的python葵花宝典这本书籍纪录对象
增加数据建议用方式一!
2.1方式一:直接实例化一个类对象————注意objects有很多方法,create是用来创建表的
注意create方法可以返回一个对象~需要查的话就不需要再进行查操作了~直接调用create方法返回的对象进行查找就OK了!

使用方法:

#注意日期按照这个格式来!
book_obj=models.Book.objects.create(title='Python葵花宝典',state=True,price=100,publish='苹果出版社',pub_date='2015-12-13')
#打印title...
print(book_obj.title)
##注意create是有返回值的,create方法的返回值book_obj就是插入book表中的python葵花宝典这本书籍纪录对象

推荐使用方式一~

2.2方式二:
book_obj=Book(title="python葵花宝典",state=True,price=100,publish="苹果出版社",pub_date="2012-12-12")
book_obj.save()
2.3方式三:批量插入
 book_list = []
    for i in range(10):
        bk_obj = models.Book(
            name='chao%s'%i,
            addr='北京%s'%i
        )
        book_list.append(bk_obj)

    models.Book.objects.bulk_create(book_list) #批量插入,速度快
2.4方式四:update_or_create——有就更新,没有就创建
obj,created = models.UserToken.objects.update_or_create(
    user=user, # 查找筛选条件
    defaults={ # 添加或者更新的数据
      "token":random_str,
    }
    )    

说明:

1、第一个值obj为:新创建的model对象
2、第二个值created为:是否进行了新的数据的插入的操作,只是更新原始数据为false,新加入数据为true!
3、如果查询到多条数据,那么就会报错!因为源码里面用的是get方法!可以用异常处理!

3、查询表记录

查询的API

***前4个最重要,5--9比较容易,10中的三个方法比较难也非常重要!
***一定要知道每个方法的返回值是什么,以及每个方法是由谁来调用的
(1)all()

models.Book.objects来调用,返回的是QuerySet类型的对象——django自己定义的数据类型
将查询出来的查询出来的所有对象放在列表中
在models的Book类中写了__str__方法,且__str__返回的是title的值,所以后面会打印title

book_list = models.Book.objects.all()
print(book_list)
#遍历这个字典可以进行相应的取值操作
for i in book_list:
    print(i.title,i.price)
#也可以进行索引操作
print(book_list[0].pub_date)
(2)first与last

返回值不是QuerySet对象,而是model对象,等价于book_list[0]
调用者是QuerySet对象

book_first = models.Book.objects.first()
print(book_first)
(3)filter方法

对应where语句
调用者objects,返回值是QuerySet对象

    book_list2 = models.Book.objects.filter(price=100)
    print(book_list2)

(3-1)可以跟first或者last方法

book_list2_last = models.Book.objects.filter(price=100).last()
print(book_list2_last.pubdate)

(3-2)可以带多个多虑条件

book_list2_more = models.Book.objects.filter(price=100,title='西游记')
print(book_list2_more)
(4)get方法

很像filter,但是,get方法有且只有一个查询结果是才有意义;如果有多个查询结果会报错!
返回值是一个model对象,利用objects调用!

book_get = models.Book.objects.get(title='西游记')
print(book_get)
(5)exclude方法

排除——得到的是个QuerySet对象,由objects调用,也可以用QuerySet对象调用
models调用:

book_exclude = models.Book.objects.exclude(title='西游记')
print(book_exclude)

QuerySet调用:

ret = models.Book.objects.filter(price=11).exclude(id=2)
print(ret)
(6)order_by排序

得到的是QuerySet对象,由objects调用
(6-1)默认升序

book_order_by_asc = models.Book.objects.order_by('title')
print('升序:',book_order_by_asc)

(6-2)降序排序

book_order_by_desc = models.Book.objects.order_by('-title')
print('降序:',book_order_by_desc)

(6-3)也可以利用两个字段排序:——第一个字段相等的时候再用第二个字段排序

book_order_by1 = models.Book.objects.order_by('title','price')
print('两个字段排序:',book_order_by1)
(7)reverse反转

可以在order_by的基础上加上reverse

book_reverse = models.Book.objects.order_by('title').reverse()
print('排序反转:',book_reverse)
(8)count计数

返回int类型的数据,调用者是QuerySet

count = models.Book.objects.all().count()
print('数据的总数:',(count,type(count)))
(9)exists检测是否存在记录

如果不加exists则表示取出来所有的值了,没必要取所有的值,这样效率不高;
加上exists相当于利用limit限制只取出来一条数据去判断有没有记录。

ret = models.Book.objects.all().exists()
if ret:
    print('OK!有数据!')
重点:(10)values(field)、values_list(field)、distinct()
下面这三个十分重要

(10-1)values(*field)
查询所有书籍的名称:
得到的是一个QuerySet对象: <QuerySet [{'title': 'Python葵花宝典'}, {'title': 'Python葵花宝典1'}, {'title': '金瓶瓶'}]>
由QuerySet对象调用
但是列表中放的不是一个个的对象了,而是一个个的字典!

book_titles = models.Book.objects.all().values('title')
print('所有书籍的名称:',book_titles)
    """
     values的工作原理:
    现在将values中的参数改为为'title','price'(注意value与value_list中可以放两个参数)
    temp = []
    for obj in models.Book.objects.all()
        temp.append(
            'title':obj.title,
            'price':obj.price
        )
    return temp 
    """

(10-1-1)也可以利用操作字典的方法来操作结果:

book_title_1_title = book_titles[1].get('title')
print('第二个书籍的名字:',book_title_1_title)   

(10-2)values_list(*field)方法
与value方法一样,调用者与返回值均是QuerySet对象
但是,value_list的结果是列表里面嵌套元组

book_values_list = models.Book.objects.all().values_list('title')
print(book_values_list)

(10-3)distinct方法
去重

price_distinct = models.Book.objects.all().values('price').distinct()
print('price去重:',price_distinct)

4、查询表记录之带双下划线的模糊查询

注意要用filter过滤
(1)查询价格大于100的:gt
gt_100 = models.Book.objects.filter(price__gt=100)
print('价格大于100的:',gt_100)

注意大于等于的话是gte:

gte_100 = models.Book.objects.filter(price__gte=100)
(2)小于:lt
lt_10000 = models.Book.objects.filter(price__lt=10000)
print('价格小于10000的:',lt_10000)
(3)大于100小于10000的:
gt_100_lt_10000 = models.Book.objects.filter(price__gt=100,price__lt=10000)
print('价格大于100小于10000的:',gt_100_lt_10000)
(4)价格包含[100,200,300]这几个的:
price_in = models.Book.objects.filter(price__in=[100,200,300])
print('价格包含:',price_in)
(5)title以“西”字开头的数据:
title_starts_xi = models.Book.objects.filter(title__startswith='西')
print('title以西字开头:',title_starts_xi)
(6)包含——contains
title_contains = models.Book.objects.filter(title__contains='p')
print('title包含p的:',title_contains)
(6-1)忽略大小写的包含——icontains
title_icontains = models.Book.objects.filter(title__icontains='p')
print('title包含p或者P的:',title_icontains)
(7)在一个范围之内:range
price_range = models.Book.objects.filter(price__range=[100,10000])
print('价格在一个范围之内:',price_range)
(8)关于日期的模糊查询
(8-1)过滤一下出版日期是2014年的数据
###注意只有date类型的字段才有__year
###想查找月份就用__month就可以了
pub_date_2014 = models.Book.objects.filter(pub_date__year=2014)
print('2014出版的数据:',pub_date_2014)

注意的问题:

#找2012年的所有书籍
all_books = models.Book.objects.filter(pub_date__year=2012)

#找大于2012年的所有书籍
all_books = models.Book.objects.filter(pub_date__year__gt=2012)


#找2019年月份的所有书籍,如果明明有结果,你却查不出结果,是因为mysql数据库的时区和咱们django的时区不同导致的,
#你需要做的就是将django中的settings配置文件里面的USE_TZ = True改为False,就可以查到结果了,
#以后这个值就改为False,而且就是因为我们用的mysql数据库才会有这个问题,其他数据库没有这个问题。
all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2)

5、删除记录与修改记录

(1)删除————delete

(1-1)方法一:QuerySet对象调用delete方法

models.Book.objects.filter(price=100).delete()

(1-2)方法二:用model对象

models.Book.objects.filter(price=100).first().delete()
(2)修改————update

注意必须要用QuerySet对象调用

models.Book.objects.filter(title='西游记').update(title='水浒传')
posted on 2019-05-18 21:53  江湖乄夜雨  阅读(151)  评论(0编辑  收藏  举报