diango入门(持续更新中)
下载
- 命令行 - pip install django==1.11.26 -i https://pypi.tuna.tsinghua.edu.cn/simple 
- pycharm 
创建项目
- 命令行 - django-admin startproject 项目名 
- pycharm - file _> new project _> django _> 输入路径 _> 选择解释器 _> create 
启动项目
- 命令行 - python manage.py runserver # 127.0.0.1:8000 - python manage.py runserver 80 # 127.0.0.1:80 - python manage.py runserver 0.0.0.0:80 # 0.0.0.0:80 
- pycharm - 点绿三角 django 前面是dj - 可以修改配置 可以改IP和端口 
使用顺序
settings 静态文件配置
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  #文件夹根目录
DEBUG = True   #因为启动出错调试模式 上线后改为Flase,出错不报错,给用户更好的用户体验
ALLOWED_HOSTS = ['*'] #让所有的用户可以访问
INSTALLED_APPS = [       #app注册
	'app01',
    'app01.apps.App01Config'   # 推荐写法
]
MIDDLEWARE = [
    # 'django.middleware.csrf.CsrfViewMiddleware',   #提交POST请求注释一个中间件
]
"""
如果使用sqlite3数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
"""
"""
#如果使用mysql数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   #diango的服务器引擎
        'NAME': 'bookmanager',
        'USER': 'root',
        'PASSWORD': '123',
        'HOST': '127.0.0.1',
        'PORT': 3306,
    }
}
#默认使用pymysql模块 替换
import pymysql
pymysql.install_as_MySQLdb()
"""
LANGUAGE_CODE = 'en-us'       #汉语 'zh-hans'  
TIME_ZONE = 'UTC'   #时区:英国的,改为亚洲上海 'Asia/Shanghai'  
STATIC_URL = '/static/'    #静态文件夹的别名,配置静态文件要以/static/为开头
STATICFILES_DIRS = [                  # 路由
    os.path.join(BASE_DIR,'static1'),   # 存放静态文件的路径
    os.path.join(BASE_DIR,'static1'),
]
				 # join(BASE_DIR必须是这个,静态文件夹static1)
					# 加上静态文件的存放路径
models.py 映射关系
写映射关系操作数据库
settings 配置:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', #使用mysql数据库引擎
        'NAME': 'day43',		#默认操作的库
        'HOST': '127.0.0.1',	#库所在的服务器地址
        'PORT': 3306,			#端口
        'USER': 'root',			#用户名
        'PASSWORD': '123',		#密登密码
    }
}
#默认使用pymysql模块 替换  也可以写在day43的__init__.py里
import pymysql
pymysql.install_as_MySQLdb()
创库
用可视化工具创建一个MySQL数据库
创表
class User(models.Model):
    username = models.CharField(max_length=32)  # varchar(32)
    password = models.CharField(max_length=32)  # varchar(32)
#类 对象   属性
#表 数据行 字段
python manage.py  makemigrations  # 制作迁移文件 出现在migrations
python manage.py migrate  # 执行SQL语句 同步
		#terminal里执行命令
△id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
urls.py 路由
路径 函数转换
==settings配置:
STATICFILES_DIRS = [                  # 路由
    os.path.join(BASE_DIR,'static1'),   # 存放静态文件的路径
    os.path.join(BASE_DIR,'static2'),
]
url(r^'用户输入输入的路径 0.0.0.0:80/index/',不加括号的函数名)
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),   # 路径和函数的对应关系,
    url(r'^modal/', modal),
]
views.py 函数
def index(request):  		    # 函数里写处理逻辑,request拿到网络路径,固定写法
    print(request.path_info)	# 打印网络路
import 导入:
导入返回字符串的包HttpResponse,返回html文件的包render,返回执行另一个逻辑的包
from django.shortcuts import HttpResponse,render, redirect
导入dpp01文件中的创建数据表的类的models.py文件
from app01 import models
return 返回:
return HttpResponse('
index
')
返回字符串
return HttpResponse('<h1>index</h1>')         # 返回字符串
return render(,,)
返回接受到请求的网络路径要渲染的html文件和要传的值
return render(request, 'index.html',{'k1':v1})
#返回   (网络路径必添,要渲染的html文件,{别名:要传的值})
return redirecct('路径')
执行另一个逻辑
ORM的操作
写函数来实现增删该查
.all 获取所有的数据
all_publishers = models.Publisher.objects.all()
#变量 = 文件名.类名.对象.取值方法
# objects.all() 查询出所有的出版社的信息,是一个对象列表
.filter(pk=pk) 获取所一个数据
第一个pk列名,第二个pk为从request中get到的
变量 = models.User.objects.filter(password='dsb')   # 对象列表
# 用.first取第一个,若无,返回空列表,if 变量;判断时,不会报错,只是不会执行这个语句了
# 用[0]取值时,,若无,取不到值,if 判断时变量,会报错
get 获取所一个数据
变量 = models.User.objects.get(password='dsb')  # 对象 特点 获取不到或者获取到多个都报错
create(name=pub_name) 添加数据
利用类的对象
obj = models.Publisher.objects.create(name=pub_name)
update(name=pub_name) 跟新数据
templates HTML文件
模板
某.html的settings配置
MIDDLEWARE = [
    # 'django.middleware.csrf.CsrfViewMiddleware',   #提交POST请求注释一个中间件
]
hyml中的样式路径配置
<head>
    <title>Title</title>
    <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.min.css">
    <script src="/static/js/jquery.js"></script>
    <script src="/static/plugins/bootstrap-3.3.7-dist/js/bootstrap.js"></script>
</head>
模板语法
△登录
<div class="container">
    <form class="form-signin" method="post" action="" novalidate>
        <h2 class="form-signin-heading">Please sign in</h2>
        <label for="inputEmail" class="sr-only">用户名</label>
        <input type="text" id="inputEmail" class="form-control" name="username" placeholder="输入用户名" required=""
               autofocus="">
        <label for="inputPassword" class="sr-only">密码</label>
        <input type="password" id="inputPassword" class="form-control" name="password" placeholder="输入密码" required="">
        <div>{{ error }}</div>
        <div class="checkbox">
            <label>
                <input type="checkbox" value="remember-me"> Remember me
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block">登录</button>
    </form>
△查询所有的作者信息
    all_authors = models.Author.objects.all()
    for author in all_authors:
        print(author)
        print(author.name)
        print(author.books,type(author.books))  # 关系管理对象
        print(author.books.all(),type(author.books.all()))  # 所关联的所有的对象
△for循环
views.py传过来的参数:
render(request,'pub.html',{'all_publishers':all_publishers})
{{ all_publishers }} 
html的for循环:
{% for i in all_publishers  %}
	{{ forloop.counter }}
	{{ i }}
	{{ i.id }}  {{ i.pk }}
	{{ i.name }}
{% endfor %}
△if
{% if 条件 %}
	xxx
{% else %}
	xxxxx
{% endif %}
△form的注意点
- form标签的属性 action='提交的地址' method='post' novalidate 取消input标签自带的校验
- input标签必须要有name属性 有些标签有value值
- 需要有一个button按钮 或者 type='submit'的input
△get 和 post
get : 获取一个页面
1.直接在浏览器的地址栏中输入地址 回车
2.form表单 不指定method
3.a标签
参数: ?k1=v1&k2=v2
获取参数: request.GET.get('k1')
post : 提交数据
form表单  method = 'post'
获取数据: request.POST.get('k1')
static1 渲染
一个项目的结构
day43项目
.idea 配置
pycharm自动帮配的配置,打开别人的diango项目时要先删了此项
app01
方便在一个大的Django项目中,管理实现不同的业务功能
migrations 放表
放和数据库同步的表
admin.py 后台
后台管理:
1、应用注册
2、admin界面汉化
apps.py 元数据
应用程序设置
元数据
models.py
写映射关系操作数据库
它包含了你存储的数据的重要字段和行为
- 每个模型都是一个Python类,它是django.db.models.Model的子类。
- 模型的每个属性都代表一个数据库字段。
#类 对象   属性
#表 数据行 字段
class User(models.Model):
    username = models.CharField(max_length=32) #varchar
    password = models.CharField(max_length=32) #varchar

tests.py 测试
views.py
写函数逻辑
day43项目包
init.py 自动执行
#默认使用pymysql模块 替换  也可以写在settings.py里
import pymysql
pymysql.install_as_MySQLdb()
settings
静态文件配置
urls.py
路径 函数转换
wsgi.py 网关接口
WSGI(Python Web Server Gateway Intergace)
中文名:python服务器网关接口,python应用于web服务器之间的接口,很少用到,但是最好不要修改其内容
static1
templates
manage.py 命令行接口
应用的命令行接口
将Django project放到sys.path目录中,同时设置DJANGO_SETTINGS_MODULE环境变量为当前project的setting.py文件。
diango运行流程
Django处理一个请求的流程:
- 在浏览器的地址栏中输入地址,回车,发了一个GET请求
- wsgi模块接收了请求,将请求的相关信息封装成request对象
- 根据地址找到对应函数
- 执行函数获取到返回结果,wsgi模块将结果返回给浏览器
发请求的途径:
- 在浏览器的地址栏中输入地址 get请求
- a标签 get请求
- form表单 get/post
reqeust
 reqeust.GET url上携带的参数 ?k1=v1&k2=v2
 reqeust.POST form表单提交POST请求的参数
 request.method 请求方式 GET、POST
response
 HttpResponse('字符串') ——》 返回字符串
 render(request,'模板的名字',{}) ——》 返回一个页面
 redirect('地址') ——》 重定向
创建一个app
terminal里执行命令
python manage.py startapp app名称
注册
settings配置
INSTALLED_APPS = [
	'app01',
    'app01.apps.App01Config'   # 推荐写法
]
Django使用MySQL数据库的流程
手动创建一个MySQL数据库
配置数据库
ENGINE   MySQL
NAME	数据库的名字
HOST    ip
PORT    3306
USER    用户名
PASSWORD  密码
在与settings同级目录下的__init__.py中写代码:
import pymysql
pymysql.install_as_MySQLdb()
写models:
form django.db import models
class Publisher(models.Model):
	name = models.CharField(max_length=32)
执行迁移的命令
python  manage.py  makemigrations  # 检查已经注册的APP下面的models.py的变更情况
python manage.py migrate   # 执行迁移
ORM 对象关系映射
对应关系:
 类 _> 表
 对象 _> 数据行(记录)
 属性 _> 字段
ORM能做的事情:对数据做修改、对表做修改
常用的字段
AutoField  自增字段
IntegerField  整数
CharField 字符串
DatetimeField  DateField 日期时间
	auto_now:每次修改时修改为当前日期时间。
	auto_now_add:新创建对象时自动添加当前日期时间。
BooleanField  布尔值
TextField   大文本
ImageField  图片
DecimalField   10进制小数
字段参数
null=True   # 数据库中该字段可以为空
blank=True  # 用户输入可以为空
db_column   # 数据库字段的名
default     # 默认值
primary_key  # 主键
db_index    # True 索引
unique      # 唯一约束
verbose_name   # 中文提示
choices        # 让用户选择的数据
建表的参数
    class Meta:
        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "person"
        # # admin中显示的表名称
        verbose_name = '个人信息'
        # verbose_name加s
        verbose_name_plural = '所有用户信息'
        # 联合索引
        # index_together = [
        #     ("name", "age"),  # 应为两个存在的字段
        # ]
        #
        # # 联合唯一索引
        unique_together = (("name", "age"),)  # 应为两个存在的字段
ORM的操作
from django.db import models
class Publisher(models.Model):
    name = models.CharField(max_length=32)
# 一对多的关系
class Book(models.Model):
    name = models.CharField(max_length=32)
    pub = models.ForeignKey(to='Publisher',on_delete=None) 
    def __str__(self):
        return self.name
查询
from app01 import models 
models.Publisher.objects.all()  #查询所有的数据  QuerySet  对象列表
models.Publisher.objects.get(name='xxx')
					#查询一条数据  只能查有且唯一的数据
models.Publisher.objects.filter(name='xxx')
					#查询所有满足条件的数据 对象列表 
for book in all_books:
    print(book)
    print(book.id)
    print(book.pk)
    print(book.name)
    print(book.pub)  # 所关联的出版社对象    	对象.外键
    print(book.pub_id)  # 所关联的出版社对象的id   对象.外键_id
新增
obj = models.Publisher.objects.create(name='xxxx')
models.Book.objects.create(name=book_name,
                           pub=models.Publisher.objects.get(pk=pub_id))
obj = models.Book.objects.create(name=book_name, pub_id=pub_id)
obj = models.Publisher(name='xxxx')
obj.save()  # 保存到数据库
obj = models.Book(name='xxx',pub_id=出版社的对象id)
obj.save()
多对多的新增:
书对作者
book_id = request.POST.getlist('book_id')
# 插入数据
obj = models.Author.objects.create(name=author_name)
obj.books.set(book_id) # 设置多对多的关系
删除
models.Publisher.objects.filter(pk=pk).delete()   # 批量删除
models.Publisher.objects.get(pk=pk).delete()	# 单条数据的删除
更新
models.Book.objects.filter(pk=pk).update(name=book_name,pub_id=pub_id)  # 批量更新
obj = models.Book.objects.filter(pk=1).first()
obj.name = 'xxxx'
obj.pub_id = 2
# book_obj.pub  =  出版社的对象
obj.save()  #保存更新
外键
一对多的关系
class Book(models.Model):
    name = models.CharField(max_length=32)
    pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
    """
    on_delete 在2.0版本之后是必填的
    on_delete=
        models.CASCADE  级联删除
        PROTECT   保护
        SET(1)
        SET_DEFAULT  设置为默认值设置为某一个值
        SET_NULL   设置为空
        DO_NOTHING 什么都不变
    """
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
# 基于对象的查询
# 正向查询
book_obj = models.Book.objects.get(pk=3)
# print(book_obj)
# print(book_obj.pub)
# 反向查询
# 没有指定related_name  表名小写_set
pub_obj = models.Publisher.objects.get(pk=2)
# print(pub_obj.book_set,type(pub_obj.book_set))  # 关系管理对象
print(pub_obj.book_set.all())
# related_name='books'
# print(pub_obj.books.all())
# 基于字段的查询
ret = models.Book.objects.filter(pub__name='老男孩出版社')
# print(ret)
# 没有指定related_name  类名小写__字段
ret = models.Publisher.objects.filter(book__name='没有页码的python书')
# 指定related_name  related_name__字段
ret = models.Publisher.objects.filter(books__name='没有页码的python书')
# 指定related_query_name='book'  related_query_name_name__字段
# ret = models.Publisher.objects.filter(book__name='没有页码的python书')
# print(ret)
多对多
class Pulisher(models.Model):
	name = models.CharField(max_length=32)
class Book(models.Model):
	name = models.CharField(max_length=32)
    pub = models.ForeignKey('Pulisher',on_delete=models.DO_NOTHING)
class Author(models.Model):
	name = models.CharField(max_length=32)
	books= models.ManyToManyField(to='Book')
# 查询
book_obj.pub   # 所关联的对象   book_obj.pub_id  所关联的对象的id
author_obj.books  # 关系管理对象
author_obj.books.all()   # 所关联的所有的书籍对象
# 新增
Book.objects.create(name='xxx',pub=对象)
Book.objects.create(name='xxx',pub_id=对象的ID)
obj= Book(name='xxx',pub_id=对象的ID)
obj.save()
obj = Author.objects.create(name='xxx')
obj.books.set([书籍id,书籍id])
# 删除
Book.objects.filter(pk=pk).delete()  # QuerySet 删除
Author.objects.get(pk=pk).delete()   # 对象 删除
# 编辑
Book.objects.filter(pk=pk).update(name='xxx')
book_obj.name ='xxxx'
book_obj.save()
Author.objects.filter(pk=pk).update(name='xxx')
author_obj.books.set([id,id])
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
author_obj = models.Author.objects.get(pk=1)
# print(author_obj.books.all())
book_obj = models.Book.objects.get(pk=1)
# print(book_obj.author_set.all())
ret = models.Author.objects.filter(books__name='没有页码的python书')
ret = models.Book.objects.filter(author__name='bigbao')
# print(ret)
# set 设置多对多的关系  [id]   [对象]
# author_obj.books.set([3,])
# author_obj.books.set( models.Book.objects.all()) # [对象,对象]
# add 添加多对多的关系  id   对象
# author_obj.books.add(1,2,3)
# author_obj.books.add(*models.Book.objects.all())  # *[对象,对象]
# remove 删除多对多的关系  id   对象
# author_obj.books.remove(1,2)
# author_obj.books.remove(*models.Book.objects.filter(pk__in=[1,2])) # [对象,对象]
# clear 清除多对多的关系
# author_obj.books.clear()
# create 新增一个所关联的对象 并且和当前的对象设置关系
author_obj.books.create(name='跟和尚学合气道', pub_id=1)
# book_obj.author_set
# 多对一  反向查询 一 ——》 多  关系管理对象
# 关系管理对象.set([对象,对象])
# 关系管理对象.add(对象,对象)
#  外键字段 null=True, 才有remove,clear
# 关系管理对象.remove(对象,对象)
# 关系管理对象.clear()
# 关系管理对象.create()
必知必会13条
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
# all()  查询所有的数据  QuerySet 对象列表
ret = models.Person.objects.all()
# filter  获取满足条件的所有的对象  QuerySet 对象列表
ret = models.Person.objects.filter(name='alex')
# exclude  获取不满足条件的所有的对象  QuerySet 对象列表
ret = models.Person.objects.exclude(name='alex')
# values()
#  不指定字段 获取所有字段名和值  QuerySet 对象列表  [ {},{} ]
#  指定字段  values('pid','name')  获取指定字段名和值  QuerySet 对象列表  [ {},{} ]
ret = models.Person.objects.all().values()
ret = models.Person.objects.filter(name='alex').values('pid', 'name')
# values_list()
#  不指定字段 获取所有的值  QuerySet 对象列表  [ (),() ]
#  指定字段  values_list('pid','name')  获取指定的值  QuerySet 对象列表  [ (),() ]
ret = models.Person.objects.all().values_list()
ret = models.Person.objects.filter(name='alex').values_list('name', 'pid', )
# order_by  排序 默认升序  -降序  可以多个字段排序
ret = models.Person.objects.all().order_by('age', '-pid')
# reverse  对已经排序的QuerySet做的反转
ret = models.Person.objects.all().order_by('pid').reverse()
# get    获取满足条件的一个的对象  对象
ret = models.Person.objects.get(name='alex')
# first  获取第一个元素   对象  获取不到的时候是none
ret = models.Person.objects.filter(name='xxx').first()
# last  获取最后一个元素   对象  获取不到的时候是none
ret = models.Person.objects.filter(name='xxx').last()
#  count 计数
ret = models.Person.objects.all().filter(age=84).count()
# exists 数据是否存在
ret = models.Person.objects.filter(age=84).exists()
# distinct  去重  数据时完全一样才去重
ret = models.Person.objects.filter(age=84).values('age').distinct()
"""
返回对象列表
all
filter
exclude
order_by
reverse
values   [{}]
values_list   [()]
distinct  
返回对象
get
first
last 
返回数字
count
返回布尔值
exists
"""
单表的双下划线
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
ret = models.Person.objects.filter(pk__gt=3)  # greater than   where pk > 3
ret = models.Person.objects.filter(pk__gte=3)  # greater than equal   where pk >= 3
ret = models.Person.objects.filter(pk__lt=3)  # less than   where pk < 3
ret = models.Person.objects.filter(pk__lte=3)  # less than equal   where pk <= 3
ret = models.Person.objects.filter(pk__range=[1,3])  # 1 <= pk <= 3
ret = models.Person.objects.filter(pk__in=[1,3,7,10,100])  # 成员判断
ret = models.Person.objects.filter(name__contains='bigbao')  # like   不忽略大小写
ret = models.Person.objects.filter(name__icontains='bigbao')  # like  ignore  忽略大小写
ret = models.Person.objects.filter(name__startswith='b')  # 以什么开头 不忽略大小写
ret = models.Person.objects.filter(name__istartswith='b')  # 以什么开头 忽略大小写
ret = models.Person.objects.filter(name__endswith='o')  # 以什么结尾 不忽略大小写
ret = models.Person.objects.filter(name__iendswith='o')  # 以什么结尾 忽略大小写
ret = models.Person.objects.filter(age__isnull=False)   # 字段是否为空
ret = models.Person.objects.filter(birth__year='2019')   # 按照年份
ret = models.Person.objects.filter(birth__contains='2019-12-19')  # 时间包含
print(ret)
外间的操作
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
# 基于对象的查询
# 正向查询
book_obj = models.Book.objects.get(pk=3)
# print(book_obj)
# print(book_obj.pub)
# 反向查询
# 没有指定related_name  表名小写_set
pub_obj = models.Publisher.objects.get(pk=2)
# print(pub_obj.book_set,type(pub_obj.book_set))  # 关系管理对象
print(pub_obj.book_set.all())
# related_name='books'
# print(pub_obj.books.all())
# 基于字段的查询
ret = models.Book.objects.filter(pub__name='老男孩出版社')
# print(ret)
# 没有指定related_name  类名小写__字段
ret = models.Publisher.objects.filter(book__name='没有页码的python书')
# 指定related_name  related_name__字段
ret = models.Publisher.objects.filter(books__name='没有页码的python书')
# 指定related_query_name='book'  related_query_name_name__字段
# ret = models.Publisher.objects.filter(book__name='没有页码的python书')
# print(ret)
聚会和分组
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
from django.db.models import Max, Min, Avg, Sum, Count
ret = models.Book.objects.filter(id__gt=3).aggregate(max=Max('price'), min=Min('price'))
# 统计每一本书的作者个数
ret = models.Book.objects.annotate(Count('author'))  # 按照book进行分组  统计作者的个数
# 统计出每个出版社的最便宜的书的价格
ret = models.Publisher.objects.annotate(Min('book__price')).values()
ret = models.Book.objects.values('pub', 'pub__name').annotate(Min('price'))
# 统计作者的图书的个数
ret = models.Author.objects.annotate(Count('books')).values()
ret = models.Book.objects.values('author').annotate(Count('id'))
# 统计不止一个作者的图书
ret = models.Book.objects.annotate(count=Count('author')).filter(count__gt=1)
# 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret = models.Book.objects.annotate(count=Count('author')).order_by('-count')
# 查询各个作者出的书的总价格
ret = models.Author.objects.annotate(Sum('books__price')).values()
ret = models.Book.objects.values('author').annotate(Sum('price'))
print(ret)
F
可以拿字段出来对比
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
from django.db.models import F, Q
# ret = models.Book.objects.filter(sale__gt=F('kucun'))
# ret = models.Book.objects.update(sale=F('sale')*2)
print(ret)
Q
| 或
- & 与
- 非
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
from django.db.models import F, Q
ret = models.Book.objects.exclude(pk__lte=3, pk__gte=2)
ret = models.Book.objects.filter(Q(~Q(Q(pk__gt=3) & Q(pk__lt=2))) & Q(name__contains='跟'))
print(ret)
事务
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()  # 初始化
from app01 import models
from django.db import transaction
try:
    with transaction.atomic():
        # 一系列的ORM操作
        models.Publisher.objects.create(name='xxx')
        models.Publisher.objects.create(name='xxx')
except Exception as e:
    print(e)
print('xxx')
cookie
定义
 保存在浏览器本地上一组组键值对
特点
1.cookie是由服务器进行设置的
2.浏览器保存在本地的
3.下次访问时自动携带对应的cookie
设置
response.set_cookie(key,value)   # Set-Cookie: is_login=1; Path=/
response.set_signed_cookie('is_login','1',salt='xxxx')
#两种操作方式相同,第二种推荐,salt='xxxx',以xxxx来进行加盐
response.set_signed_cookie(key,value,salt='xxxx',max_age=5,path='/')
	#max_age=5,让浏览器记住登录状态的时间最大为五秒,超过五秒清楚is_login
获取
request.COOKIES #  {}
request.COOKIES[key]# 没有会报错
request.COOKIES.get(key)
request.get_signed_cookie(key,salt='xxxx',default='')
#default='' 默认值设置为空,不然取不到会报错
删除
response.delete_cookie(key)  # 设置键值对的值为空 超时时间为0
装饰器
让装了装饰器的函数的名字不是inner,而是原来的名字
from functools import wraps
def wrapper(func):
    @wraps(func)  # 复制了原来函数的名字和注释
	def inner(request,*arg,**kwargs):
        # 之前
        ret = func(request,*arg,**kwargs)
        # 之后
        return ret
    return inner
@wrapper  # f1 = wrapper(f1)
def f1(request):
    pass
f1.__name__  #  inner _> f1   #让装了装饰器的函数的名字不是inner,而是原来的名字
response
diango中有三种response
from django.shortcuts import render, redirect, HttpResponse
HttpResponse()
render()
redirect()
session
定义
保存在服务器上的一组组键值对,必须依赖cookie
为什么要有session?
- cookie保存在浏览器上,不太安全
- cookie的大小和个数有限制
设置
request.session[key] = value
request.session[is.login]=1
获取
request.session[key] #这样取值没有取值会报错
request.session.get(key)
删除
request.session.pop('is_login')
request.session.delete()  #  删除所有的session数据
request.session.flush()   #  删除所有的session数据和cookie
其他
request.session.clear_expired()		#清除过期的session  ?
request.session.set_expiry(value)   #设置session过期的时间
配置
from django.conf import global_settings
#在global_settings中查看全局配置
#在settings中覆盖修改
SESSION_COOKIE_NAME = 'session'   # cookie的名字
SESSION_SAVE_EVERY_REQUEST = True  # 每次请求都更新session
SESSION_EXPIRE_AT_BROWSER_CLOSE = True  # 浏览器关闭session就失效
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
from django.contrib.sessions.backends import db #session在哪储存
# 数据库  缓存  缓存+数据库  文件 加密cookie
正则表达式
^ 开头
$ 结尾
[0-9] 数字
[a-zA-Z] 英文字母
[asd]{4,6}
+(一个或多个)
*(0个或多个)
? (0个或1个)
\d 阿拉伯数字
\w   匹配字母或数字或下划线或汉字 等价于'[^A-Za-z0-9_]。
.(除了换行符之外的任意字符)
url的命名和反向解析
静态路由
url(r'^login/', views.login,name='login'),
反向解析ht
模板
{% url 'login' %}   ——》  '/app01/login/'
py文件
from django.urls import reverse
reverse('login')   ——》  '/app01/login/'
分组路由
位置传参
url(r'^del_publisher/(\d+)/', views.del_publisher,name='del_pub'),
#分组后从url中捕获参数,捕获的参数会按照 位置传参 传递给函数
关键字传参
url(r'^del_publisher/(?P<pk>\d+)/', views.del_publisher),
#分组后从url中捕获参数,捕获的参数会按照 关键字传参 传递给函数
反向解析
模板
{% url 'del_pub' '1' %}   ——》  '/app01/del_publisher/1/'
py文件
from django.urls import reverse
reverse('del_pub',args=('1',))   ——》  '/app01/del_publisher/1/'
命令分组 路由
url(r'^del_publisher/(?P<pk>\d+)/', views.del_publisher,name='del_pub'),
反向解析
模板
{% url 'del_pub' '1' %}   ——》  '/app01/del_publisher/1/'     位置传参
{% url 'del_pub' pk='1' %}   ——》  '/app01/del_publisher/1/'  关键字传参
py文件
from django.urls import reverse
reverse('del_pub',args=('1',))   ——》  '/app01/del_publisher/1/'    位置传参
reverse('del_pub',kwargs={'pk':'1'})   ——》  '/app01/del_publisher/1/'  关键字传参
namespace
多人合作开发解决路径重复问题
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/',include('app01.urls',namespace='app01' )),
    url(r'^app02/',include('app02.urls',namespace='app02')),
]
{% url 'app01:del_pub' '1' %}
reverse('app01:del_pub',args=('1',))
MVC
M: model 模型 与数据库交互
V: view 视图 HTML
C:controller 控制器 流程 和 业务逻辑
MTV
M:model ORM
T:template 模板 HTML
V:view 视图 业务逻辑
Django中的视图
FBV
def add_publisher(request,*args,**kwargs):
	# 逻辑
	return  response
urls.py
  url(r'^add_publisher',add_publisher )
CBV
from django.views import View
class Addpublisher(View):
	def get(self,reuqest,*args,**kwargs)
        # 处理GET请求的逻辑
        self.request
        return  response
    def post(self,reuqest,*args,**kwargs)
        # 处理POST请求的逻辑
        return  response
urls.py
  url(r'^add_publisher',Addpublisher.as_view() )
CBV中不同的请求能找到相应函数执行的原因:
继承了View,程序加载的时候,执行View中的Addpublisher.as_view(),
Addpublisher.as_view()定义了一个view函数,返回view,通过反射获取请求方式对应的方法(get/post)。
as_view的流程:
1. 程序加载的时候,执行Addpublisher.as_view():
   定义了一个view函数,返回view
   url(r'^add_publisher',view )
2. 请求到来时,执行view函数:
   1. 实例化对象  ——》 self
   2. self.request = request
   3. 执行self.dispatch(request, *args, **kwargs)
      1. 判断请求方式是否被允许:
         1. 允许: 通过反射获取请求方式对应的方法(get/post)  ——》 handler
         2. 不允许:self.http_method_not_allowed  ——》 handler
      2. 执行handler,将它的结果返回
加装饰器
FBV
FBV 直接加
@login_required
def publisher(request):
CBV
解决装饰器加同时加在类和函数上时的参数导致的复用性问题:
不加method_decorator,加在类方法上,需要第一个参数需要是self,如果在login_required的inner括号里加上了self,函数便无法用这个装饰器了,所以装饰器加在类上时要借助method_decorator
from django.utils.decorators import method_decorator
登录验证的装饰器
def login_required(func):
    def inner(request, *args, **kwargs):
        # print(request.COOKIES)
        # is_login = request.COOKIES.get('is_login')
        # is_login = request.get_signed_cookie('is_login', salt='xxxx', default='')
        is_login = request.session.get('is_login')
        # print(request.path_info)
        # print(request.get_full_path())
        print(request.META.get('HTTP_ACCEPT'))
        print(is_login)
        if is_login != 1:
            # 没有登录 跳转到登录页面
            url = request.path_info
            return redirect('/login/?url={}'.format(url))
        ret = func(request, *args, **kwargs)
        return ret
        # if is_login == '1':
        #     # 登录
        #     ret = func(request, *args, **kwargs)
        #     return ret
        # else:
        #     url = request.path_info
        #     return redirect('/login/?url={}'.format(url))
    return inner
# 加在方法上
@method_decorator(login_required)
def get(self, request, *args, **kwargs):
# 重写dispatch方法,加在dispatch方法上
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
    ret = super().dispatch(request, *args, **kwargs)
    return ret
#直接找到父类的dispatch方法加上去 ——最简单!推荐!
@method_decorator(login_required,name='dispatch')
class Addpublisher(View)
# 加在类上
@method_decorator(login_required,name='post')
@method_decorator(login_required,name='get')
class Addpublisher(View)
request对象的方法
request.method  # 请求方法 GET POST
request.GET     # url上携带的参数  {}
request.POST   # form提交POST请求的参数  {}   编码类型是urlencode
request.body   # 请求体  b''
request.path_info  # 路径  不包含ip和端口  也不包含参数
request.COOKIES  # cookie的字典
request.session  # session的数据
request.FILES  # 上传的文件  编码的类型是  enctype="multipart/form-data"
request.META     # 请求头   小写 ——》 大写  HTTP_    - _> _
request.get_full_path()   # 路径  不包含ip和端口  包含参数
request.get_signed_cookie(key)  # 获取加密cookie
request.is_ajax()   # 是否是ajax
response对象的方法
HttpResponse('xxxx')  # 返回字符串   Content-Type: text/html; charset=utf-8
render(request,'模板的路径',{})   # 返回一个页面
redirect('路径')   # 重定向  状态码301 302  响应头 Location:路径
from django.http.response import JsonResponse
模板中
变量 {{ 变量名 }}
通过(.)取相应的内容
.key > .属性 .方法 > .索引
过滤器
{{ 变量|过滤器 }} {{ 变量|过滤器:参数 }}
{{ kong | default:'没有传参' }}
filter
{{ 变量|filter:参数 }}
default add length slice join first last lower upper title truncatechars truncatewords
date:"Y-m-d H:i:s" safe
USE_L10N = False
DATETIME_FORMAT = 'Y-m-d H:i:s'
标签
{% %}
for
{%  for i in list %}
	{{ i }}
{% endfor %}
forloop.counter 循环的序号 从1开始
forloop.counter0 循环的序号 从0开始
forloop.revcounter 循环的序号 到1结束
forloop.revcounter0 循环的序号 到0结束
forloop.first 是否是第一次循环
forloop.last 是否是最后一次循环
forloop.parentloop 当前循环的外层循环 {}
{% for i in list  %}
	{{ forloop }}
	{{ i }}
{% empty %}
	空空如也
{% endfor %}
if 不支持算数运算
{% if  条件  %}
	x
{% elif  条件1  %}
	xx
{% else %}
	xxx
{% endif %}
注意点:
- 不支持算数运算
- 不支持连续判断 10 > 5 > 1 false
csrf
{% csrf_token %}  #  form表单中有一个隐藏的input标签  name=‘csrfmiddlewaretoken’
母版和继承
母版:
 模板,提取出多个页面公共部分放在一个母版中,定义上多个block块,让子页面重新复写。
继承:
- {% extends ‘母版的名字’  %}
 2. 重写block块。
注意点:
1. {% extends ‘母版的名字’  %}  母版名字 的引号好带上   不带会当做变量
- {% extends ‘母版的名字’ %} 上不要写内容 - 要替换母版的内容都写在block块中 
- 定义多个block块, css,js 
 
组件:
把一小段的HTML代码段 ——》 nav.html
{% include 'nav.html ' %}
静态文件的使用:
{% load static %}
"{% static '静态文件的相对路径' %}"
母版和组件的继承和引用怎么用?
使用admin的步骤
- 创建超级用户 - python manage.py createsuperuser 
- 注册model - 在app下的admin.py中注册 - from django.contrib import admin
 from app01 import models
 # Register your models here.
 admin.site.register(models.Person)
 
- 访问网站登录操作 
中间件
process_request(self,request)
参数:
 request 请求的对象,和视图中的request是同一个
执行时间:视图函数之前
执行顺序:
 按照注册的顺序 顺序执行
返回值
 None : 正常流程
 HttpReponse: 当前中间件之后的中间件的process_request方法、路由匹配、视图也不执行,直接执行当前中间件的process_response方法
process_response(self, request, response)
参数:
 request 请求的对象,和视图中的request是同一个
 response 返回的响应对象
执行时间:视图函数之后
执行顺序:
 按照注册的顺序 倒叙执行
返回值
 HttpReponse: 必须返回响应对象
process_view(self, request, view_func, view_args, view_kwargs)
参数:
 request 请求的对象,和视图中的request是同一个
 view_func 视图函数
 view_args 视图函数的位置参数
 view_kwargs 视图函数的关键字参数
执行时间:路由匹配之后,视图函数之前
执行顺序:
 按照注册的顺序 顺序执行
返回值
 None 正常流程
 HttpReponse: 当前中间件之后的process_view、视图不执行,执行最后一个中间的process_response
process_exception(self, request, exception)
参数:
 request 请求的对象,和视图中的request是同一个
 exception 错误对象
执行时间(触发条件):视图层面有错误才执行
执行顺序:
 按照注册的顺序 倒叙执行
返回值
 None 当前中间没有处理异常,交由下一个中间件处理异常,所有的中间件都没有处理,Django处理错误
 HttpReponse: 当前中间处理好异常,之后执行最后一个中间件的process_response方法
pocess_template_response(self,request,response)
参数:
 request 请求的对象,和视图中的request是同一个
 response 返回的响应对象
执行时间:视图必须返回一个template_response对象
执行顺序:
 按照注册的顺序 倒叙执行
返回值
 HttpReponse: 必须返回响应对象
response.template_name = 'index1.html'  # 改模板
response.context_data # 改变量

\1. 简介目录
https://www.cnblogs.com/maple-shaw/p/9029086.html
\2. 路由系统
https://www.cnblogs.com/maple-shaw/articles/9282718.html
\3. 视图
https://www.cnblogs.com/maple-shaw/articles/9285269.html
\4. 模板
https://www.cnblogs.com/maple-shaw/articles/9333821.html
5.ORM
字段和参数:https://www.cnblogs.com/maple-shaw/articles/9323320.html
查询操作:https://www.cnblogs.com/maple-shaw/articles/9403501.html
练习题:https://www.cnblogs.com/maple-shaw/articles/9414626.html
\6. cookie和session
https://www.cnblogs.com/maple-shaw/articles/9502602.html
7.中间件
https://www.cnblogs.com/maple-shaw/articles/9333824.html
\8. ajax
https://www.cnblogs.com/maple-shaw/articles/9524153.html
\9. form组件
https://www.cnblogs.com/maple-shaw/articles/9537309.html
AJAX
js技术,发送请求的一种方式.
特点:
- 异步
- 传输的数据量小
- 局部刷新
发请求的途径:
- form表单发请求  指定method   GET/POST
- action 地址 method enctype
- input select option 标签要有name属性,有的还需要有value
- 有button按钮或者type='submit'的input框
 
- 直接在地址栏中输入地址 回车 get
- a标签 get
- ajax
发ajax请求的写法:
jQuery:
$.ajax({
    url: '/test/',   //  url路径
    type: 'post',    // 请求方式
    data: {          // 请求数据
        name: 'alex',
        age: 84,
        hobby: JSON.stringify(['吐口水', 'TESA', '篮球', '毒鸡汤']),
    },
    success: function (res) {   // 响应成功的回调函数
        console.log(res);
        console.log(res.status);
        console.log(typeof(res))
    },
    error:function (res) {		// 响应失败的回调函数
        console.log(res)
    }
})
上传文件
$('#b1').click(function () {
        var form_obj = new FormData();  // enctype="multipart/form-data"
        form_obj.append('name', 'alex')
        form_obj.append('f1', $('#f1')[0].files[0])
        $.ajax({
            url: '/upload/',
            type: 'post',
            data: form_obj,
            processData: false, // 不需要处理编码方式
            contentType: false, // 不需要处理contentType请求头
            success:function (res) {  //  响应成功的回调函数  res _> 返回的响应体
                alert(res)
            }
        })
    })
CSRF中间件
- process_request方法: - 从cookie中获取csrftoken的值,放到request.META中 
- process_view方法: - 判断视图是否使用csrf_exempt装饰器,使用了就不校验
- 判断请求方式是否是'GET', 'HEAD', 'OPTIONS', 'TRACE',如果是,也不校验
- 进行校验:
- csrf_token = request.META.get('CSRF_COOKIE') # cookie中获取csrftoken的值
 
- 请求方式是POST
- request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
- 获取不到,从头中获取x-csrftoken的值 = >request_csrf_token
- 进行比较csrf_token  request_csrf_token的值:
- 对比成功 接收请求
- 对比不成功 拒绝请求
 
 
 
ajax通过Django的csrf校验
前提:
有csrftoken的cookie:
- {% csrf_token %} 
- from django.views.decorators.csrf ensure_csrf_cookie
 
方式一:
给data中添加csrfmiddlewaretoken的键值对
方式二:
给headers添加x-csrftoken的键值对
headers: {
'x-csrftoken': $('[name="csrfmiddlewaretoken"]').val()},
方式三:
导入文件
csrf相关的装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie
csrf_exempt   豁免csrf的校验   CBV需要加在dispatch方法上才生效
csrf_protect  强制校验
ensure_csrf_cookie  添加csrftoken的cookie
form组件
定义:
from django import forms
class RegForm(forms.Form):
    user = forms.CharField(label='用户名')
    pwd = forms.CharField(label='密码',widget=forms.PasswordInput)
使用:
视图函数:
def reg2(request):
    form_obj = RegForm()
    if request.method == 'POST':
        form_obj = RegForm(request.POST)
        if form_obj.is_valid():
            # 校验成功
            # form_obj.cleaned_data # 清洗后的数据
            return HttpResponse('注册成功')
    return render(request, 'reg2.html', {'form_obj': form_obj})
模板:
{{ form_obj.as_p }}   _>       生成所有字段的input框
{{ form_obj.user }}         _> 某个字段的input框
{{ form_obj.user.label }}   _> 某个字段的中文提示
{{ form_obj.user.id_for_label }}   _> 某个字段的input框的ID
{{ form_obj.errors }}   _>  所有的错误
{{ form_obj.user.errors }}  _>  某个字段的所有错误
{{ form_obj.user.errors.0 }}  _>  某个字段的第一个错误
常用的字段
CharField
ChoiceField
MultipleChoiceField
字段的参数
initial  初始值
required  是否必填
disabled  是否禁用
label 	  中文提示
initial   默认值
min_length  最小长度
error_messages   错误信息
choices   可选择的数据
校验
- 自带的校验 
- 自定义校验规则 - 写函数 - from django.core.exceptions import ValidationError def check_name(value):
 # 自定义校验规则
 # 如果校验合格 什么都不做
 # 校验不合格 抛出异常
 if 'alex' in value:
 raise ValidationError('不能包含alex,非法字符')
 
- 使用内置的校验器 - from django.core.validators import RegexValidator validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式不正确')]
 
局部钩子和全局钩子
def clean_user(self):
    # 局部钩子
    # 校验成功  返回当前字段的值
    # 校验不成功  抛出异常
    if 'alex' in self.cleaned_data.get('user'):
        raise ValidationError('不能包含alex,非法字符')
    return self.cleaned_data.get('user')
def clean(self):
    # 全局钩子
    # 校验成功  返回所有字段的值 self.cleaned_data
    # 校验不成功  抛出异常
    pwd = self.cleaned_data.get('pwd')
    re_pwd = self.cleaned_data.get('re_pwd')
    if pwd == re_pwd:
        return self.cleaned_data
    else:
        self.add_error('re_pwd','两次密码不一致!!!')
        raise ValidationError('两次密码不一致')
diango入门(持续更新中)的更多相关文章
- 前端深入之js篇丨Array数组操作从入门到成神Up Up Up,持续更新中
		写在前面 随着前端深入的不断学习,发现数组这个数据结构在前端中有着相当大的存在感,由于我初学前端的时候并没有系统性的学习数组,所以我将通过这篇文章同你一起学习数组,希望我们能一起进步,学会熟练操作数组 ... 
- java视频教程  Java自学视频整理(持续更新中...)
		视频教程,马士兵java视频教程,java视频 1.Java基础视频 <张孝祥JAVA视频教程>完整版[RMVB](东西网) 历经5年锤炼(史上最适合初学者入门的Java基础视频)(传智播 ... 
- git常用命令(持续更新中)
		git常用命令(持续更新中) 本地仓库操作git int 初始化本地仓库git add . ... 
- Atom使用记录(持续更新中)
		部分内容取自:http://www.jianshu.com/p/dd97cbb3c22d,我自己也在使用,持续更新中 Atom安装插件在窗口中File---Setting---install 在里面进 ... 
- Pig基础学习【持续更新中】
		*本文参考了Pig官方文档以及已有的一些博客,并加上了自己的一些知识性的理解.目前正在持续更新中.* Pig作为一种处理大规模数据的高级查询语言,底层是转换成MapReduce实现的,可以作为MapR ... 
- Pig语言基础-【持续更新中】
		***本文参考了Pig官方文档以及已有的一些博客,并加上了自己的一些知识性的理解.目前正在持续更新中.*** Pig作为一种处理大规模数据的高级查询语言,底层是转换成MapReduce实现的, ... 
- 系列文章:老项目的#iPhone6与iPhone6Plus适配#(持续更新中,更新日期2014年10月12日 星期日 )
		本文永久地址为http://www.cnblogs.com/ChenYilong/p/4020399.html ,转载请注明出处. ********************************** ... 
- 知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET
		知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET undefined 公司介绍 - 数人科技 undefined 
- Python开发【第二十三篇】:持续更新中...
		Python开发[第二十三篇]:持续更新中... 
随机推荐
- JS的引入方式_变量的使用_变量的类型
			JS的俩种引入方式: 1. <!--js的引入方式1--> <script> /*网页中的弹框*/ alert("js的学习!!") </script ... 
- [TimLinux] JavaScript 模态框可拖动功能实现——jQuery版
			<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ... 
- Geoserver2.15.1 配置自带 GeoWebCache 插件发布 ArcGIS Server 瓦片(附配置好的 Geoserver2.15.1 下载)
			之前写过一篇关于 Geoserver2.8.5 版本的部署配置发布 ArcGIS Server 瓦片点击查看,那是下载 Geoserver2.8.5 源码编译,重新打包 jar 来部署配置思路的,版本 ... 
- k8s 开船记-触礁:四涡轮发动机撞坏3个引发502故障
			(图片来自网络) 非常抱歉,这次开船触礁故障给您带来麻烦了,请您谅解. 在我们昨天发布 k8s 开船记首航博文后,有园友在评论中发来贺词——“泰坦尼克号出发了[狗头]”,借此吉言,今天船就触礁了,还好 ... 
- CoderForces999F-Cards and Joy
			F. Cards and Joy time limit per test 2 seconds memory limit per test 256 megabytes input standard in ... 
- 5分钟搞清楚Synchronized和Lock的概念与区别
			前言 并发编程中,锁是经常需要用到的,今天我们一起来看下Java中的锁机制:synchronized和lock. Synchronized 和 Lock的概念 Synchronized 是Java 并 ... 
- GHOST CMS -上下文概述 Context Overview
			Context Overview上下文概述 Each page in a Ghost theme belongs to a context, which determines which templa ... 
- 计算机等级考试真题2(JAVA)
			答案: 解析: (注:解析部分是博主用所学知识以及在百度上搜索总结出来的) 1. D (A)类属于JAVA语言的引用数据类型. (B)接口属于JAVA语言的引用数据类型. (C)数组属于JAVA语 ... 
- C语言笔记 08_函数指针&回调函数&字符串&结构体&位域
			函数指针 函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: / ... 
- 通过 loganalyzer 展示数据库中的系统日志
			目录 通过 loganalyzer 展示数据库中的日志 环境准备 准备服务器: 日志服务器: 数据库服务器: 测试日志服务器和数据库是否连接: websrv服务器端: 通过 loganalyzer 展 ... 
