Django学习笔记以及项目制作过程

pip install django

创建项目

进入d:\projects 目录,执行 django-admin startproject bysms

创建 如下的目录结构:

bysms/ 项目根目录

manage.py 一个工具脚本,用作项目管理的

bysms/ 目录是python包。 里面包含项目的重要配置文件。

init.py

settings.py Django 项目的配置文件. 包含了非常重要的配置项

urls.py 里面存放了 一张表, 声明了前端发过来的各种http请求

wsgi.py

wsgi规范的 web后端系统

Django学习笔记以及项目制作过程

运行django web服务

在d:\projects\bysms\执行命令 python manage.py runserver 0.0.0.0:80

在浏览器中输入环回地址127.0.0.1

添加其他域名地址:

Django学习笔记以及项目制作过程

创建项目app

进入d:\projects根目录,执行python manage.py startapp sales 命令

就会创建一个目录名为sales的app

Django学习笔记以及项目制作过程

Django学习笔记以及项目制作过程

返回页面内容给浏览器

在views.py中写入

from django.http import HttpResponse

def listorders(request):

        return HttpResponse("下面是系统中所有的订单信息。。。")

Django学习笔记以及项目制作过程

添加url路由记录

Django学习笔记以及项目制作过程
from django.contrib import admin
from django.urls import path

别忘了导入 listorders 函数
from sales.views import listorders

urlpatterns = [
    path('admin/', admin.site.urls),

    # 添加如下的路由记录
    path('sales/orders/', listorders),
]

登录查看

登录浏览器输入

网址http://127.0.0.1/sales/orders/

简化路由表,路由表拆分

Django学习笔记以及项目制作过程

所以在sales目录下面建立一个新的文件sales\urls.py

然后在sales\urls.py 文件中输入:

from django.urls import path

from sales.views import listorders

urlpatterns = [
    path('orders/', listorders),
]

Django学习笔记以及项目制作过程

在总的urls的文件中就需要修改路由路径

Django学习笔记以及项目制作过程

数据库和表

sqlite 没有 独立的数据库服务进程。Django中可以直接使用,无须先搭建数据服务。

项目中数据库的配置在bysms/settings.py 中,这里

Django学习笔记以及项目制作过程

根目录下一般有一个db.sqlite3 文件,但是是0KB,

然后创建数据库执行python manage.py migrate命令,里面就有东西了

然后下载数据库工具sqlitestudio,地址:

SQLiteStudio Django学习笔记以及项目制作过程https://sqlitestudio.pl/;

进去打开即可

ORM

定义一张数据库的表 就是定义一个继承自 django.db.models.Model 的类

定义该表中的字段(列), 就是定义该类里面的一些属性

类的方法就是对该表中数据的处理方法,包括 数据的增删改查

定义数据库

先创建一个名为common的应用目录,进入根目录执行python manage.py startapp common 命令

在common/models.py中,定义我们的业务所需要的表。

max_length 指明了该 varchar字段的 最大长度。

from django.db import models

class Customer(models.Model):
    # 客户名称
    name = models.CharField(max_length=200)

    # 联系电话
    phonenumber = models.CharField(max_length=200)

    # 地址
    address = models.CharField(max_length=200)

创建数据库表

在项目的配置文件 settings.py 中, INSTALLED_APPS 配置项 加入如下内容

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

    # 加入下面这行
    'common.apps.CommonConfig',
]

在项目根目录下执行d:\projects\bysms>python manage.py makemigrations common命令

然后在common\migrations 目录下面自动出现了0001_initial.py,就是相应要进行的数据库操作代码。

再在项目根目录下执行 d:\projects\bysms>python manage.py migrate命令

如果后面需要更新一个数据,

比如加一个QQ上去,需要在后面加上两个参数null = True , blank = True

然后执行python manage.py makemigrations common命令和python manage.py migrate命令

创建管理员Admin

项目的根目录,执行d:\projects\bysms>python manage.py createsuperuser命令

然后在黑窗口上自己输入登录名、email、密码(密码至少8个字符)

需要修改应用里面的 管理员 配置文件 common/admin.py,注册我们定义的model类

from django.contrib import admin

from .models import Customer

admin.site.register(Customer)

然后再运行一下python manage.py runserver 0.0.0.0:80

进入到浏览器,访问 http://127.0.0.1/admin/

这里可以直接添加信息。去数据库查看就可以删除、增加、修改用户信息

如果要改网站语言,在配置文件 settings.py 中 MIDDLEWARE 最后加入如下配置

 # admin界面语言本地化
    'django.middleware.locale.LocaleMiddleware',

读取数据库数据

实现浏览器访问 sales/customers/,服务端就返回系统中所有的客户记录给浏览器。

在文件sales/views.py 中,定义一个listcustomers 函数,(别写错了views的上级目录)

导入 Customer 对象定义
from  common.models import  Customer

def listcustomers(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    # 每条表记录都是是一个dict对象,
    # key 是字段名,value 是 字段值
    qs = Customer.objects.values()

    # 定义返回字符串
    retStr = ''
    for customer in  qs:
        for name,value in customer.items():
            retStr += f'{name} : {value} | '

        #  表示换行
        retStr += ''

    return HttpResponse(retStr)

要注意加上路由请求,修改 sales/urls.py就行了,已经拆分过路由了。

加上 path(‘customers/’, listcustomers),别忘了导入

Django学习笔记以及项目制作过程

然后在浏览器输入如下 网址:http://127.0.0.1/sales/customers/

如果编辑过customer

就会得到

Django学习笔记以及项目制作过程

过滤数据库信息

当用户在浏览器输入/sales/customers/?phonenumber=13000000001 ,要求返回电话号码为 13000000001 客户记录。

修改sales/views.py的代码

def listcustomers(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Customer.objects.values()

    # 检查url中是否有参数phonenumber
    ph =  request.GET.get('phonenumber',None)

    # 如果有,添加过滤条件
    if ph:
        qs = qs.filter(phonenumber=ph)

    # 定义返回字符串
    retStr = ''
    for customer in  qs:
        for name,value in customer.items():
            retStr += f'{name} : {value} | '
        #  表示换行
        retStr += ''

    return HttpResponse(retStr)

request.GET就是获取到了一个字典,request.GET.get就是找到键为第一个参数的值,如果没有就返回None

qs = qs.filter(phonenumber=ph),这个类似于Django去执行查询的SQL的语句 加上where从句,进行过滤查询

现在在浏览器输入如下 url:http://127.0.0.1/sales/customers/?phonenumber=1354784574

就会得到

Django学习笔记以及项目制作过程

前后端分离架构

代码直接生成HTML

方案一:用字符串拼接形式

修改sales/views.py的代码

先定义好HTML模板
html_template ='''

table {
    border-collapse: collapse;
}
th, td {
    padding: 8px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

        id
        姓名
        电话号码
        地址

        %s

'''

def listcustomers(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Customer.objects.values()

    # 检查url中是否有参数phonenumber
    ph =  request.GET.get('phonenumber',None)

    # 如果有,添加过滤条件
    if ph:
        qs = qs.filter(phonenumber=ph)

    # 生成html模板中要插入的html片段内容
    tableContent = ''
    for customer in  qs:
        tableContent += ''

        for name,value in customer.items():
            tableContent += f'{value}'

        tableContent += ''

    return HttpResponse(html_template%tableContent)

替换好了之后,再次访问 http://127.0.0.1/sales/customers/

就会出现表格类型的页面

方案二:使用模板

修改sales/views.py的代码

先定义好HTML模板
html_template ='''

table {
    border-collapse: collapse;
}
th, td {
    padding: 8px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

        id
        姓名
        电话号码
        地址

        {% for customer in customers %}

            {% for name, value in customer.items %}
                {{ value }}
            {% endfor %}

        {% endfor %}

'''

from django.template import engines
django_engine = engines['django']
template = django_engine.from_string(html_template)

def listcustomers(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Customer.objects.values()

    # 检查url中是否有参数phonenumber
    ph =  request.GET.get('phonenumber',None)

    # 如果有,添加过滤条件
    if ph:
        qs = qs.filter(phonenumber=ph)

    # 传入渲染模板需要的参数
    rendered = template.render({'customers':qs})

    return HttpResponse(rendered)

然后,访问浏览器,可以得到一样的结果。

后端不涉及HTML,

如果采用前后端分离的架构开发, 后端几乎不负责任何展现界面的工作,只负责对数据进行管理 。

创建mgr应用目录

针对管理员用户的请求,

前面为 销售员用户专门创建了一个应用 sales 来处理相关的请求。

再为 管理员用户 专门创建一个应用 mgr 来处理相关的 请求。

执行python manage.py startapp mgr命令

添加处理请求模块和url路由

在bysms/mgr应用里,添加一个新的文件customer.py文件

在这个文件中定义一个函数,用来分发路径

在 customer.py 中定义如下 dispatcher 函数

from django.http import JsonResponse
import json

def dispatcher(request):
    # 将请求参数统一放入request 的 params 属性中,方便后续处理

    # GET请求 参数在url中,同过request 对象的 GET属性获取
    if request.method == 'GET':
        request.params = request.GET

    # POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取
    elif request.method in ['POST','PUT','DELETE']:
        # 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式
        request.params = json.loads(request.body)

    # 根据不同的action分派给不同的函数进行处理
    action = request.params['action']
    if action == 'list_customer':
        return listcustomers(request)
    elif action == 'add_customer':
        return addcustomer(request)
    elif action == 'modify_customer':
        return modifycustomer(request)
    elif action == 'del_customer':
        return deletecustomer(request)

    else:
        return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})

修改路由表

在bysms/urls.py中,修改api/mgr/的路由

凡是 url 以 api/mgr  开头的,
都根据 mgr.urls 里面的 子路由表进行路由
path('api/mgr/', include('mgr.urls')),

Django学习笔记以及项目制作过程

然后再去mgr 目录下面添加 urls.py 路由文件

from django.urls import path

from mgr import customer

urlpatterns = [

    path('customers', customer.dispatcher),
]

凡是 API 请求url为 /api/mgr/customers 的,都交由 我们上面定义的dispatch函数进行分派处理

在mgr/customers.py中写入函数

def listcustomers(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Customer.objects.values()

    # 将 QuerySet 对象 转化为 list 类型
    # 否则不能 被 转化为 JSON 字符串
    retlist = list(qs)

    return JsonResponse({'ret': 0, 'retlist': retlist})

开头需要导入

导入 Customer
from common.models import Customer

Django学习笔记以及项目制作过程

最后返回的数据就是这样的格式。

添加客户

在mgr/customers.py中写入函数

def addcustomer(request):

    info    = request.params['data']

    # 从请求消息中 获取要添加客户的信息
    # 并且插入到数据库中
    # 返回值 就是对应插入记录的对象
    record = Customer.objects.create(name=info['name'] ,
                            phonenumber=info['phonenumber'] ,
                            address=info['address'])

    return JsonResponse({'ret': 0, 'id':record.id})

Customer.objects.create方法就可以添加一条Customer表里面的记录。

临时取消CSRF校验

为了简单起见,我们先临时取消掉CSRF的 校验机制,等以后有需要再打开。

在 项目的配置文件bysms/settings.py 中 MIDDLEWARE配置项 里 注释掉 ‘django.middleware.csrf.CsrfViewMiddleware’

Django学习笔记以及项目制作过程

修改客户

在mgr/customers.py中写入函数

def modifycustomer(request):

    # 从请求消息中 获取修改客户的信息
    # 找到该客户,并且进行修改操作

    customerid = request.params['id']
    newdata    = request.params['newdata']

    try:
        # 根据 id 从数据库中找到相应的客户记录
        customer = Customer.objects.get(id=customerid)
    except Customer.DoesNotExist:
        return  {
                'ret': 1,
                'msg': f'id 为{customerid}的客户不存在'
        }

    if 'name' in  newdata:
        customer.name = newdata['name']
    if 'phonenumber' in  newdata:
        customer.phonenumber = newdata['phonenumber']
    if 'address' in  newdata:
        customer.address = newdata['address']

    # 注意,一定要执行save才能将修改信息保存到数据库
    customer.save()

    return JsonResponse({'ret': 0})

删除客户

在mgr/customers.py中写入函数

def deletecustomer(request):

    customerid = request.params['id']

    try:
        # 根据 id 从数据库中找到相应的客户记录
        customer = Customer.objects.get(id=customerid)
    except Customer.DoesNotExist:
        return  {
                'ret': 1,
                'msg': f'id 为{customerid}的客户不存在'
        }

    # delete 方法就将该记录从数据库中删除了
    customer.delete()

    return JsonResponse({'ret': 0})

和前端集成

前端系统:点击这里下载Django学习笔记以及项目制作过程http://cdn1.python3.vip/files/django/z_dist.zip ;,解压到文件根目录

在 bysms/urls.py 文件中,在末尾 添加一个

+ static("/", document_root="./z_dist")

并添加声明

静态文件服务
from django.conf.urls.static import static

Django学习笔记以及项目制作过程

重新启动一下,输入python manage.py runserver 80

然后再打开浏览器,输入如下网址:http://localhost/mgr/index.html

实现登录

在mgr目录里面创建一个代码文件sign_in_out.py

from django.http import JsonResponse

from django.contrib.auth import authenticate, login, logout

登录处理
def signin( request):
    # 从 HTTP POST 请求中获取用户名、密码参数
    userName = request.POST.get('username')
    passWord = request.POST.get('password')

    # 使用 Django auth 库里面的 方法校验用户名、密码
    user = authenticate(username=userName, password=passWord)

    # 如果能找到用户,并且密码正确
    if user is not None:
        if user.is_active:
            if user.is_superuser:
                login(request, user)
                # 在session中存入用户类型
                request.session['usertype'] = 'mgr'

                return JsonResponse({'ret': 0})
            else:
                return JsonResponse({'ret': 1, 'msg': '请使用管理员账户登录'})
        else:
            return JsonResponse({'ret': 0, 'msg': '用户已经被禁用'})

    # 否则就是用户名、密码有误
    else:
        return JsonResponse({'ret': 1, 'msg': '用户名或者密码错误'})

登出处理
def signout( request):
    # 使用登出方法
    logout(request)
    return JsonResponse({'ret': 0})

创建url路由

因为在总路由文件bysms/urls.py 中 添加了路由记录

所以只需要在mgr 目录下面 的子路由文件urls.py 里添加内容

 path('signin', sign_in_out.signin),
 path('signout', sign_in_out.signout),

以及导入

from mgr import sign_in_out

然后浏览器登录 http://127.0.0.1/mgr/sign.html

测试

测试登录登出接口

在根目录创建一个新的目录test,在此目录下创建一个新的py文件,tc001.py

import  requests,pprint

payload = {
    'username': 'jiang',
    'password': '123qwe456'
}

response = requests.post('http://localhost/api/mgr/signin',
              data=payload)

pprint.pprint(response.json())

测试列出客户的接口

在test目录下创建一个新的py文件,ts001.py

import  requests,pprint

response = requests.get('http://localhost/api/mgr/customers?action=list_customer')

pprint.pprint(response.json())

测试添加客户的接口

在test目录下创建一个新的py文件,ts002.py

import  requests,pprint

构建添加 客户信息的 消息体,是json格式
payload = {
    "action":"add_customer",
    "data":{
        "name":"武汉市桥西医院",
        "phonenumber":"13345679934",
        "address":"武汉市桥西医院北路"
    }
}

发送请求给web服务
response = requests.post('http://localhost/api/mgr/customers',
              json=payload)

pprint.pprint(response.json())

构建查看 客户信息的消息体
response = requests.get('http://localhost/api/mgr/customers?action=list_customer')

发送请求给web服务
pprint.pprint(response.json())

session方案

解决用户可能会跳过登录,直接访问后续地址能直接访问的问题

修改 mgr/customer.py 的dispatcher 函数,在前面加上

    # 根据session判断用户是否是登录的管理员用户
    if 'usertype' not in request.session:
        return JsonResponse({
            'ret': 302,
            'msg': '未登录',
            'redirect': '/mgr/sign.html'},
            status=302)

    if request.session['usertype'] != 'mgr' :
        return JsonResponse({
            'ret': 302,
            'msg': '用户非mgr类型',
            'redirect': '/mgr/sign.html'} ,
            status=302)

Django学习笔记以及项目制作过程

关闭黑窗口再重新打开,并输入python manage.py runserver 80

然后再打开浏览器,输入如下网址:http://localhost/mgr/index.html

按下F12会发现,显示未登录

数据库的关联

在common/models.py删除之前创建的QQ

Django学习笔记以及项目制作过程

在控制台输入python manage.py makemigrations common

以及python manage.py migrate

一对多

表之间 一对多 的关系,就是 外键 关联关系 ForeignKey

在common/models.py新增药品类

class Medicine(models.Model):
    # 药品名
    name = models.CharField(max_length=200)
    # 药品编号
    sn = models.CharField(max_length=200)
    # 描述
    desc = models.CharField(max_length=200)

在common/models.py定义 订单 表 Order

import datetime
class Order(models.Model):
    # 订单名
    name = models.CharField(max_length=200,null=True,blank=True)

    # 创建日期
    create_date = models.DateTimeField(default=datetime.datetime.now)

    # 客户
    customer = models.ForeignKey(Customer,on_delete=models.PROTECT)
  • CASCADE

删除主键记录和 相应的外键表记录。

比如,我们要删除客户张三,在删除了客户表中张三记录同时,也删除Order表中所有这个张三的订单记录,瀑布式删除

  • PROTECT

禁止删除记录。

比如,我们要删除客户张三,如果Order表中有张三的订单记录,Django系统 就会抛出ProtectedError类型的异常,当然也就禁止删除 客户记录和相关的订单记录了。

除非我们将Order表中所有张三的订单记录都先删除掉,才能删除该客户表中的张三记录。

  • SET_NULL

删除主键记录,并且将外键记录中外键字段的值置为null。 当然前提是外键字段要设置为值允许是null。

比如,我们要删除客户张三时,在删除了客户张三记录同时,会将Order表里面所有的 张三记录里面的customer字段值置为 null。 但是上面我们并没有设置 customer 字段有 null=True 的参数设置,所以,是不能取值为 SET_NULL的。

在控制台输入python manage.py makemigrations common

以及python manage.py migrate

一对一

Django 中 用OneToOneField对象 实现 一对一 的关系

class Student(models.Model):
    # 姓名
    name = models.CharField(max_length=200)
    # 班级
    classname = models.CharField(max_length=200)
    # 描述
    desc = models.CharField(max_length=200)

class ContactAddress(models.Model):
    # 一对一 对应学生
    student = models.OneToOneField(Student, on_delete=models.PROTECT)
    # 家庭
    homeaddress = models.CharField(max_length=200)
    # 电话号码
    phone = models.CharField(max_length=200)

多对多

Django是通过 ManyToManyField 对象 表示 多对多的关系的

会新建立一个表common_ordermedicine

用两个表的键来建立关系的一个新表

Django学习笔记以及项目制作过程

在common/models.py新增

 # 订单购买的药品,和Medicine表是多对多 的关系
    medicines = models.ManyToManyField(Medicine, through='OrderMedicine')

class OrderMedicine(models.Model):
    order = models.ForeignKey(Order, on_delete=models.PROTECT)
    medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)

    # 订单中药品的数量
    amount = models.PositiveIntegerField()

Django学习笔记以及项目制作过程

在控制台输入python manage.py makemigrations common

以及python manage.py migrate

药品管理

在 mgr 目录下面新建 medicine.py

from django.http import JsonResponse

导入 Medicine 对象定义
from  common.models import  Medicine

import json

def dispatcher(request):
    # 根据session判断用户是否是登录的管理员用户
    if 'usertype' not in request.session:
        return JsonResponse({
            'ret': 302,
            'msg': '未登录',
            'redirect': '/mgr/sign.html'},
            status=302)

    if request.session['usertype'] != 'mgr':
        return JsonResponse({
            'ret': 302,
            'msg': '用户非mgr类型',
            'redirect': '/mgr/sign.html'},
            status=302)

    # 将请求参数统一放入request 的 params 属性中,方便后续处理

    # GET请求 参数 在 request 对象的 GET属性中
    if request.method == 'GET':
        request.params = request.GET

    # POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取
    elif request.method in ['POST','PUT','DELETE']:
        # 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式
        request.params = json.loads(request.body)

    # 根据不同的action分派给不同的函数进行处理
    action = request.params['action']
    if action == 'list_medicine':
        return listmedicine(request)
    elif action == 'add_medicine':
        return addmedicine(request)
    elif action == 'modify_medicine':
        return modifymedicine(request)
    elif action == 'del_medicine':
        return deletemedicine(request)

    else:
        return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})

def listmedicine(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Medicine.objects.values()

    # 将 QuerySet 对象 转化为 list 类型
    # 否则不能 被 转化为 JSON 字符串
    retlist = list(qs)

    return JsonResponse({'ret': 0, 'retlist': retlist})

def addmedicine(request):

    info    = request.params['data']

    # 从请求消息中 获取要添加客户的信息
    # 并且插入到数据库中
    medicine = Medicine.objects.create(name=info['name'] ,
                            sn=info['sn'] ,
                            desc=info['desc'])

    return JsonResponse({'ret': 0, 'id':medicine.id})

def modifymedicine(request):

    # 从请求消息中 获取修改客户的信息
    # 找到该客户,并且进行修改操作

    medicineid = request.params['id']
    newdata    = request.params['newdata']

    try:
        # 根据 id 从数据库中找到相应的客户记录
        medicine = Medicine.objects.get(id=medicineid)
    except Medicine.DoesNotExist:
        return  {
                'ret': 1,
                'msg': f'id 为{medicineid}的药品不存在'
        }

    if 'name' in  newdata:
        medicine.name = newdata['name']
    if 'sn' in  newdata:
        medicine.sn = newdata['sn']
    if 'desc' in  newdata:
        medicine.desc = newdata['desc']

    # 注意,一定要执行save才能将修改信息保存到数据库
    medicine.save()

    return JsonResponse({'ret': 0})

def deletemedicine(request):

    medicineid = request.params['id']

    try:
        # 根据 id 从数据库中找到相应的药品记录
        medicine = Medicine.objects.get(id=medicineid)
    except Medicine.DoesNotExist:
        return  {
                'ret': 1,
                'msg': f'id 为{medicineid}的客户不存在'
        }

    # delete 方法就将该记录从数据库中删除了
    medicine.delete()

    return JsonResponse({'ret': 0})

在 mgr\urls.py里面加上 对 medicine 的 请求处理 路由设置

from django.urls import path
from mgr import customer,sign_in_out,medicine

urlpatterns = [

    path('customers', customer.dispatcher),
    path('medicines', medicine.dispatcher), # 加上这行

    path('signin', sign_in_out.signin),
    path('signout', sign_in_out.signout),

]

ORM 对关联表的操作

在common/models.py中定义这样的两个Model

国家表
class Country(models.Model):
    name = models.CharField(max_length=100)

学生表, country 字段是国家表的外键,形成一对多的关系
class Student(models.Model):
    name    = models.CharField(max_length=100)
    grade   = models.PositiveSmallIntegerField()
    country = models.ForeignKey(Country,
                                on_delete=models.PROTECT)

在控制台执行

python manage.py makemigrations common

python manage.py migrate

在数据库中生成表

然后在命令行中执行 python manage.py shell,直接启动Django命令行,输入代码。

from common.models import *
c1 = Country.objects.create(name='中国')
c2 = Country.objects.create(name='美国')
c3 = Country.objects.create(name='法国')
Student.objects.create(name='白月', grade=1, country=c1)
Student.objects.create(name='黑羽', grade=2, country=c1)
Student.objects.create(name='大罗', grade=1, country=c1)
Student.objects.create(name='真佛', grade=2, country=c1)
Student.objects.create(name='Mike', grade=1, country=c2)
Student.objects.create(name='Gus',  grade=1, country=c2)
Student.objects.create(name='White', grade=2, country=c2)
Student.objects.create(name='Napolen', grade=2, country=c3)

正向关联访问

外键表字段访问:

s1 = Student.objects.get(name='白月')
s1.country.name

外键表字段过滤:

Student.objects.filter(grade=1).values()

查找Student表中所有 一年级中国 学生,不能写成 Student.objects.filter(grade=1,country=’中国’)

可以写成:

cn = Country.objects.get(name='中国')
Student.objects.filter(grade=1,country_id=cn.id).values()

也可以:

cn = Country.objects.get(name='中国')
Student.objects.filter(grade=1,country=cn).values()

更简便:

Student.objects.filter(grade=1,country__name='中国').values()

返回结果如果只需要 学生姓名 和 国家名两个字段,可以指定values内容

Student.objects.filter(grade=1,country__name='中国').values('name','country__name')

但是国家名是 country__name,格式不对,如果要求一定是countryname可以使用annotate 方法将获取的字段值进行重命名

from django.db.models import F

annotate 可以将表字段进行别名处理
Student.objects.annotate(
    countryname=F('country__name'),
    studentname=F('name')
    ).filter(grade=1,countryname='中国').values('studentname','countryname')

反向关联访问

反向关系,是通过 表Model名转化为小写 表示的

获取到所有属于这个国家的学生:

cn = Country.objects.get(name='中国')
cn.student_set.all()

通过 表Model名转化为小写 ,后面加上一个 _set 来获取所有的反向外键关联对象

反向过滤:

获取所有 具有一年级学生 的国家名:

先获取所有的一年级学生id列表
country_ids = Student.objects.filter(grade=1).values_list('country', flat=True)

再通过id列表使用  id__in  过滤
Country.objects.filter(id__in=country_ids).values()

没有指定related_name, 则应该使用 表名转化为小写

Country.objects.filter(student__grade=1).values()

但是这样会产生重复记录,可以使用 .distinct() 去重

Country.objects.filter(student__grade=1).values().distinct()

实现项目代码

在 mgr 目录下面新建order.py处理 客户端发过来的 列出订单、添加订单 的请求

from django.http import JsonResponse
from django.db.models import F
from django.db import IntegrityError, transaction

导入 Order 对象定义
from  common.models import  Order,OrderMedicine

import json

def dispatcher(request):
    # 根据session判断用户是否是登录的管理员用户
    if 'usertype' not in request.session:
        return JsonResponse({
            'ret': 302,
            'msg': '未登录',
            'redirect': '/mgr/sign.html'},
            status=302)

    if request.session['usertype'] != 'mgr':
        return JsonResponse({
            'ret': 302,
            'msg': '用户非mgr类型',
            'redirect': '/mgr/sign.html'},
            status=302)

    # 将请求参数统一放入request 的 params 属性中,方便后续处理

    # GET请求 参数 在 request 对象的 GET属性中
    if request.method == 'GET':
        request.params = request.GET

    # POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取
    elif request.method in ['POST','PUT','DELETE']:
        # 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式
        request.params = json.loads(request.body)

    # 根据不同的action分派给不同的函数进行处理
    action = request.params['action']
    if action == 'list_order':
        return listorder(request)
    elif action == 'add_order':
        return addorder(request)

    # 订单 暂 不支持修改 和删除

    else:
        return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})

在mgr\urls.py里面加上 对 orders 请求处理的路由

from django.urls import path
from mgr import customer,sign_in_out,medicine,order

urlpatterns = [

    path('customers', customer.dispatcher),
    path('medicines', medicine.dispatcher),
    path('orders', order.dispatcher), # 加上这行

    path('signin', sign_in_out.signin),
    path('signout', sign_in_out.signout),

]

事务、多对多记录添加

添加函数 addorder,来处理 添加订单 请求。

添加一条订单记录,需要在2张表(Order 和 OrderMedicine )中添加记录。

两张表的插入,意味着我们要有两次数据库操作。

如果第一次插入成功, 而第二次插入失败, 就会出现 Order表中 把订单信息写了一部分,而OrderMedicine表中 该订单的信息 却没有写成功。

这是个大问题: 就会造成 这个处理 做了一半。

那么数据库中就会出现数据的不一致。术语叫 脏数据

用 数据库 的 事务 机制来解决这个问题

Django 中可以使用with transaction.atomic()来解决

在order.py中添加

def addorder(request):

    info  = request.params['data']

    # 从请求消息中 获取要添加订单的信息
    # 并且插入到数据库中

    with transaction.atomic():
        new_order = Order.objects.create(name=info['name'] ,
                                         customer_id=info['customerid'])

        batch = [OrderMedicine(order_id=new_order.id,medicine_id=mid,amount=1)
                    for mid in info['medicineids']]

        #  在多对多关系表中 添加了 多条关联记录
        OrderMedicine.objects.bulk_create(batch)

    return JsonResponse({'ret': 0,'id':new_order.id})

ORM外键关联

在order.py中添加

def listorder(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Order.objects\
            .annotate(
                customer_name=F('customer__name'),
                medicines_name=F('medicines__name')
            )\
            .values(
                'id','name','create_date','customer_name','medicines_name'
            )

    # 将 QuerySet 对象 转化为 list 类型
    retlist = list(qs)

    # 可能有 ID相同,药品不同的订单记录, 需要合并
    newlist = []
    id2order = {}
    for one in retlist:
        orderid = one['id']
        if orderid not in id2order:
            newlist.append(one)
            id2order[orderid] = one
        else:
            id2order[orderid]['medicines_name'] += ' | ' + one['medicines_name']

    return JsonResponse({'ret': 0, 'retlist': newlist})

Original: https://blog.csdn.net/jc2935030145/article/details/122267557
Author: 小筱超
Title: Django学习笔记以及项目制作过程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/735593/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球