Django开发:(3.1)ORM:单表操作

MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动

ORM是“对象-关系-映射”的简称。

Mysql中的表对应python中的类,表的字段对应类的属性,表的记录对应类的实例化的对象

单表操作

创建表

1. 创建模型

 

创建名为app01的app,在app01下的models.py中创建模型:

from django.db import models

# Create your models here.

class Book(models.Model):  # 类名可以随便起,但一定得继承 models.Model
    id = models.AutoField(primary_key=True)  # AutoField表示自增字段; primary_key=True 表示是主键
    title = models.CharField(max_length=32)
    # state = models.BooleanField()
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish = models.CharField(max_length=32)

2. 更多字段和参数

每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。

3. settings配置

若想将模型转为mysql数据库中的表,需要在settings中配置:

DATABASES = {
    'default':{
        'ENGINE':'django.db.backends.mysql',
        'NAME':'orm', # 要连接的数据库,连接前需要先创建好
        'USER':'root',  # 连接数据库的用户名
        'PASSWORD':'tj037778',  # 连接数据库的密码
        'HOST':'127.0.0.1',  # 连接主机
        'PORT':3306  # 端口
    }
}

注意1:NAME即数据库的名字,在mysql连接前该数据库必须已经创建(ORM只能处理到表这一层,数据库操作不了),而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。然后,启动项目,会报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入:

import pymysql

pymysql.install_as_MySQLdb()

最后通过两条数据库迁移命令即可在指定的数据库中创建表 :

python manage.py makemigrations
python manage.py migrate

注意2:确保配置文件中的INSTALLED_APPS中写入我们创建的app名称

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
]

注意3:如果报错如下:

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,需要修改如下:

通过查找路径:D:\python\Lib\site-packages\django\db\backends\mysql\base.py

把里面的

if version < (1, 3, 3):
    raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

注释掉 就OK了。

注意4: 如果想打印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',
        },
    }
}

添加表记录

urls.py

from django.contrib import admin
from django.urls import path

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path(r'index/',views.index)
]

app01/models.py

from django.db import models

# Create your models here.

class Book(models.Model):  # 类名可以随便起,但一定得继承 models.Model
    id = models.AutoField(primary_key=True)  # AutoField表示自增字段; primary_key=True 表示是主键
    title = models.CharField(max_length=32)
    # state = models.BooleanField()
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish = models.CharField(max_length=32)

app01/views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

# 先把模型导入进来
from app01.models import Book

def index(request):
    # 添加表记录

    # 方式一:实例化Book对象
    book_obj = Book(id=1,title="python全栈开发",pub_date="2018-6-7",price=100,publish="IT出版社")   # pub_date是一个日期类型,一定得按照"年-月-日"的格式
    book_obj.save()  # save()之后记录才会生成 # 表里面的一条记录就是类的一个对象

    # 方式二:用 objects去调用create; create方法有返回值,返回值就是生成的记录对象
    book_obj2 = Book.objects.create(title="linux运维",pub_date="2015-6-7",price=100,publish="IT出版社")  # id是自增的,所以无需再写
    # 这种方式不需要 save();.objects.create(kwargs) 直接就在数据库中生成了一条记录(对象),并把这个对象返回给 book_obj2(我们也就可以打印book_obj2中的属性)
    print(book_obj2.pub_date)


    return HttpResponse("ok")

单表查询:

models.py

from django.db import models

# Create your models here.

class Book(models.Model):  # 类名可以随便起,但一定得继承 models.Model
    id = models.AutoField(primary_key=True)  # AutoField表示自增字段; primary_key=True 表示是主键
    title = models.CharField(max_length=32)
    # state = models.BooleanField()
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish = models.CharField(max_length=32)

    def __str__(self):  # 只是控制了对象的打印形式
        return self.title

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

# 先把模型导入进来
from app01.models import Book

def index(request):

    # #######################查询表结构API#######################
    # 注意:要知道每个方法的返回值是什么、以及每个方法是谁来调用的

    # 1. all() :查询所有结果;返回值是一个QuerySet数据类型(Django自定义的数据类型),调用者是 objects
    all_books = Book.objects.all()
    print(all_books)
    # Book类没有 __str()__方法时的打印结果:
    # <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>]>
    # Book有 __str()__ 方法时的打印结果:
    # <QuerySet [<Book: python全栈开发>, <Book: linux运维>]>
    # QuerySet数据类型:相当于 [obj1,obj2,...],可用列表的方式去处理它

    for obj in all_books:  # 支持遍历
        print(obj.title,obj.pub_date)

    print(all_books[1].publish)  # 支持索引

    # 2. first(),last()方法;返回值是一个model(模型)对象,调用者是 QuerySet对象
    book1 = Book.objects.all().first()  # 相当于 Book.objects.all()[0]
    book2 = Book.objects.all().last()

    print(book1,book2)

    # 3. filter()方法:返回值:QuerySet对象,调用者:管理器(objects)
    books = Book.objects.filter(title="python全栈开发",price=100)  # filter()的作用相当于sql语句的where;# 返回值:[obj1,obj2,....];多个过滤条件用逗号分隔
    print(books)
    # filter()方法也可以调用 first(),last()方法
    print(books.first())

    # 4. get()方法:有且只有一个查询结果时才有意义;如果有多个查询结果或者没有查询结果,报错;所以,返回值:model对象
    book_get = Book.objects.get(title="python全栈开发")
    print(book_get.price)

    # 5. exclude():排除条件的过滤,对应filter();返回QuerySet
    ret = Book.objects.exclude(title="python全栈开发")
    print(ret)

    # 6. order_by():按照某种条件排序(默认是按照id);返回值:QuerySet,调用者:QuerySet
    book_order_asc = Book.objects.all().order_by("id")
    print(book_order_asc)
    book_order_desc = Book.objects.all().order_by("-id")  # 按照降序排列
    print(book_order_desc)
    book_price_desc = Book.objects.all().order_by("-price")  # 按照价格降序排列
    print(book_price_desc)
    # Book.objects.all().order_by("-price","id") # 先按照价格降序排列,再按照id升序
    # 要熟悉orm的链式操作

    # 7. reverse() :对查询结果反向排序

    # 8. count():计数;返回值:int,调用者:QuerySet
    Book.objects.all().count()  # 计数里面有多少个元素

    # 9. exists():如果QuerySet包含数据,就返回True,否则返回False
    ret_exists = Book.objects.all().exists()  # 判断 Book.objects.all() 里面是否有数据

    # 10. values(*field):field代表字段;values()具有遍历作用,返回一个QuerySet --- 一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列;返回值:QuerySet(列表里面放字典),调用者:QuerySet
    # values()方法很重要
    ret_values = Book.objects.all().values("price","title")
    print(ret_values)
    # <QuerySet [{'price': Decimal('100.00'), 'title': 'python全栈开发'}, {'price': Decimal('100.00'), 'title': 'linux运维'}]>
    print(ret_values[0].get("price"))
    # 100.00

    # 11. values_list(*field):它与values()非常相似,它返回的QuerySet里面是一个元组序列,values返回的是一个字典序列
    ret_values_list = Book.objects.all().values_list("price", "title")
    print(ret_values_list)
    # <QuerySet [(Decimal('100.00'), 'python全栈开发'), (Decimal('100.00'), 'linux运维')]>

    # 12. distinct():从返回结果中剔除重复纪录(通常配合values,values_list一起使用)
    ret_distinct = Book.objects.all().values("price").distinct()
    print(ret_distinct)
    # <QuerySet [{'price': Decimal('100.00')}]>
    # 注: Book.objects.all().distinct() 这种写法没有任何意义

    # #######################查询表结构之模糊查询#######################
    # 1. __gt :大于;__lt:小于;返回值:QuerySet
    price__gt = Book.objects.filter(price__gt=50,price__lt=200)
    print("__gt",price__gt)

    # 2. __startswith:以什么开头;返回值:QuerySet
    obj_start = Book.objects.filter(title__startswith="py")
    print(obj_start)

    # 3. __contains:包含什么;返回值:QuerySet
    # 4. __icontains:包含某些元素(不区分大小写)
    obj_contains = Book.objects.filter(title__contains="x")
    print(obj_contains)

    # 5. __in = [] :是列表中的一个;返回值:QuerySet
    obj_in = Book.objects.filter(price__in=[100,150,200])
    print(obj_in)

    # 6. __year : 某一年的(只有date类型有);返回值:QuerySet
    obj_year = Book.objects.filter(pub_date__year=2018)
    print(obj_year)

    # 7. __range = [] : 在某个区间(包含两端)
    obj_range = Book.objects.filter(price__range=[50,100])
    print("range",obj_range)

    return HttpResponse("ok")

单表之删除和编辑

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

# 先把模型导入进来
from app01.models import Book

def index(request):
    # #######################删除、修改表记录#######################
    # delete() : 删除;调用者:QuerySet对象 或者 model对象;返回值是删除元素的一些信息
    # Book.objects.filter(pub_date__year=2018).delete()  # 需要都把记录查询出来才能删除;调用者:QuerySet对象
    # Book.objects.filter(pub_date__year=2015).first().delete()  # 调用者:model对象

    # update():编辑记录; 调用者:QuerySet
    Book.objects.filter(title__contains="linux").update(title="linux运营维护")


    return HttpResponse("ok")

图书管理系统

settings.py

"""
Django settings for bookms project.

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

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

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

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


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

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'm6=&s25aszxks#m(5f57mdpi)hc%v7#&e0$kmak48@80xr7t0h'

# 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',
    'app01.apps.App01Config',
]

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 = 'bookms.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        '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 = 'bookms.wsgi.application'


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

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#     }
# }
DATABASES = {
    'default':{
        'ENGINE':'django.db.backends.mysql',
        'NAME':'bookms', # 要连接的数据库,连接前需要先创建好
        'USER':'root',  # 连接数据库的用户名
        'PASSWORD':'tj037778',  # 连接数据库的密码
        'HOST':'127.0.0.1',  # 连接主机
        'PORT':3306  # 端口
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.0/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/2.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


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

STATIC_URL = '/static/'  # 若存放静态文件的static目录在app目录下,则改局生效,无需定义下面的
# STATICFILES_DIRS = [
#     os.path.join(BASE_DIR,"static"),
# ]    # 若存放静态文件的static目录在project目录下,则用该定义


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

urls.py

"""bookms URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path(r"addbook/",views.addbook),
    path(r"books/",views.books),

    # (\d+)用于匹配获取表记录的主键(书的id)
    re_path(r"books/(\d+)/delete",views.bookdel),
    re_path(r"books/(\d+)/edit",views.bookedit)
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

# 把模型表引入
from app01.models import Book

def addbook(request):
    # 由于templates/addbook.html 中 form标签的 action没写,所以还是提交到了当前页面index.html
    if request.method == "POST":
        title = request.POST.get("title")
        price = request.POST.get("price")
        date = request.POST.get("date")
        press = request.POST.get("press")

        # 添加记录
        book_obj = Book.objects.create(title=title,price=price,pub_date=date,publish=press)

        return redirect("/books/")

    return render(request,"addbook.html")

def books(request):
    books_list = Book.objects.all()

    return render(request,"books.html",locals())

def bookdel(request,pk):
    # 把对应ID的书删除
    Book.objects.filter(id=pk).delete()

    # redirect()的作用就是让浏览器往重定向的路径再发一次请求
    return redirect("/books/")

def bookedit(request,id):
    # 编辑对应ID的书

    # 先获取对应书对象
    book_obj = Book.objects.filter(id=id).first()

    # 由于编辑后的内容还是提交到当前页面,所以在这个函数里面要判断请求是否就POST
    if request.method == "POST":
        # 获取编辑后的内容
        title = request.POST.get("title")
        price = request.POST.get("price")
        date = request.POST.get("date")
        press = request.POST.get("press")

        # 把编辑后的内容更新到表中
        Book.objects.filter(id=id).update(title=title,price=price,pub_date=date,publish=press)
        # 更新完后重定向到 books.html 页面
        return redirect("/books/")

    return render(request,"editbook.html",{"book_obj":book_obj})

models.py

from django.db import models

# Create your models here.

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.CharField(max_length=32)

books.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>addbook</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">

    <style>
        .container{
            margin-top: 70px;
        }
        .btn{
            margin-bottom: 10px;
        }
    </style>

</head>
<body>
<h3>查看书籍</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <a href="/addbook/" class="btn btn-primary">添加书籍</a>
            {# .table-striped 类可以给 <tbody> 之内的每一行增加斑马条纹样式;.table-bordered 类为表格和其中的每个单元格增加边框。#}
            <table class="table table-striped table-bordered">
                <thead>
                    <tr>
                        <th>书籍名称</th>
                        <th>价格</th>
                        <th>出版日期</th>
                        <th>出版社</th>
                        <th>编辑</th>
                        <th>删除</th>
                    </tr>
                </thead>
                <tbody>
                    {# 遍历 books_list是的每一个对象(表记录),把每条表记录添加到 table的一行中 #}
                    {% for book in books_list %}
                        <tr>
                            <td>{{ book.title }}</td>
                            <td>{{ book.price }}</td>
                            {# 利用date过滤器格式化时间样式 #}
                            <td>{{ book.pub_date|date:"Y-m-d" }}</td>
                            <td>{{ book.publish }}</td>
                            {# 动态的为每个a标签添加索引 #}
                            <td><a href="/books/{{ book.pk }}/edit" class="btn btn-info">编辑</a></td>
                            {# 把主键id添加到a标签的路径中;.pk表示model对象的主键 #}
                            <td><a href="/books/{{ book.pk }}/delete" class="btn btn-danger">删除</a></td>
                        </tr>

                    {% endfor %}

                </tbody>
            </table>
        </div>
    </div>
</div>

</body>
</html>

addbook.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>addbook</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">

    <style>
        .container{
            margin-top: 70px;
        }
        .btn{
            margin-top: 10px;
        }
    </style>

</head>
<body>
<h3>添加书籍</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}
                <div>
                    <label for="">书籍名称</label>
                    {# form-controlform-control控件,可以为input元素添加CSS定制样式#}
                    <input type="text" class="form-control" name="title">
                </div>
                <div>
                    <label for="">价格</label>
                    <input type="text" class="form-control" name="price">
                </div>
                <div>
                    <label for="">出版日期</label>
                    {# date类型:能够下拉选择日期 #}
                    <input type="date" class="form-control" name="date">
                </div>
                <div>
                    <label for="">出版社</label>
                    <input type="text" class="form-control" name="press">
                </div>
                {# btn btn-success:绿色按钮;pull-right:右移 #}
                <input type="submit" class="btn btn-success pull-right">
            </form>
        </div>
    </div>
</div>

</body>
</html>

editbook.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>addbook</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">

    <style>
        .container{
            margin-top: 70px;
        }
        .btn{
            margin-top: 10px;
        }
    </style>

</head>
<body>
<h3>编辑书籍</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            {# form标签的action没写,所以默认还是会提交到当前页面 #}
            <form action="" method="post">
                {% csrf_token %}
                <div>
                    <label for="">书籍名称</label>
                    {# 编辑时书名这一栏的内容是该对象原先的title;其它栏同理 #}
                    <input type="text" class="form-control" name="title" value="{{ book_obj.title }}">
                </div>
                <div>
                    <label for="">价格</label>
                    <input type="text" class="form-control" name="price" value="{{ book_obj.price }}">
                </div>
                <div>
                    <label for="">出版日期</label>
                    {# 此处的日期也需要用date过滤器格式化,要不然显示不出来 #}
                    <input type="date" class="form-control" name="date" value="{{ book_obj.pub_date|date:'Y-m-d' }}">
                </div>
                <div>
                    <label for="">出版社</label>
                    <input type="text" class="form-control" name="press" value="{{ book_obj.publish }}">
                </div>
                {# btn btn-success:绿色按钮;pull-right:右移 #}
                <input type="submit" class="btn btn-success pull-right">
            </form>
        </div>
    </div>
</div>

</body>
</html>

猜你喜欢

转载自www.cnblogs.com/neozheng/p/9151510.html