2021-03-24
-
跨源资源共享(Cross-Origin Resource Sharing),CORS是一种基于HTTP头的机制,该机制通过允许服务器标识除了它自己以外的其他origin(源是协议、主机名和端口号的组合),这样其他origin就可以加载这些资源。
-
XMLHttpRequest和Fetch API遵循同源策略。
-
Access-Control-Allow-Origin: *
:允许所有origin访问该资源。
同源策略
#
- 同源是指协议相同、主机名相同、端口相同。
- 非同源会限制Cookie和Ajax。
访问控制场景
#
- 浏览器将CORS请求分为两类,简单请求和非简单请求。
简单请求
#
- 如果请求的HTTP方法是GET、HEAD、POST,并且HTTP头部除了被用户代理自动设置的首部字段外,只有Accept-Language、Content-Language、Content-Type ;并且Content-Type 的值仅限于下列三者之一: text/plain、multipart/form-data、application/x-www-form-urlencoded。那么Web浏览器发出的是简单请求。发出简单请求时,该请求将像一般请求一样发送到服务器。
- 简单请求不会触发CORS 预检请求。
预检请求
#
- 预检请求会先使用
OPTION
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。预检请求的使用可以避免跨域请求对服务器的用户数据产生未预期的影响。当预检不通过,实际的请求将不会发送。
HTTP响应首部
#
Access-Control-Allow-Origin
#
Access-Control-Allow-Origin: <origin> | *
- origin参数的值指定了允许访问该资源的外域URL,对不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
- 如果服务器指定了具体的域名而非"*",那么响应首部中的Vary字段的值必须包含Origin。这将告诉客户端,服务器对不同的源站返回不同的内容。
- 在跨源访问时,XMLHttpRequest对象的getResponseHeaders方法只能拿到Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,需要服务器设置响应头。
- Accsess-Control-Expose-Headers头让服务器把允许浏览器访问的头放入白名单。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Max-Age
#
Access-Control-Max-Age: 3600
Access-Control-Allow-Credentials
#
- Access-Control-Allow-Credentials 头指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容。当用在 预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials。简单 GET 请求不会被预检。
Access-Control-Allow-Methods
#
- Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Methods: <method>[, <method>]*
- Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。
参考链接:
跨资源共享(CORS)
参考链接:
跨域资源共享 - 阮一峰的网络日志 (ruanyifeng.com)
2021-03-17
- 查看集群
select * from system.clusters
- 分片shard是指包含数据不同部分的服务器。要读取所有数据,必须访问所有分片。
- 副本replica是存储复制数据的服务器,要读取所有数据,访问一个切片上的任意副本上的数据即可。
集群部署
#
-
在群集的所有机器上安装clickhouse服务端
-
在配置文件中设置群集配置
<remote_servers>
<perftest_1shards_1replicas>
<shard>
<replica>
<host>localhost</host>
<port>9000</port>
</replica>
</shard>
</perftest_1shards_1replicas>
</remote_servers>
-
在集群上创建本地表
-
在集群上创建分布式表
- 分布式表实际上是一种视图(view),从分布式表中执行select查询会使用集群所有分片的资源。
internal_replication
:
- 当参数为true时,写操作只选择一个正常的副本写入数据。如果表是复制表Replicated*Merge,请使用此方案。
- 当为false时,写操作会将数据写入所有副本。因为不会检查副本一致性,随着时间的推移,副本数据可能不一样。
数据副本
#
- 对于INSERT和ALTER语句操作数据时会在压缩的情况下被复制。
- CREATE、DROP、ATTACH、DETACH和RENAME语句只会在单个服务器上执行,不会被复制。
- 如果配置文件中没有设置ZooKeeper,则无法创建复制表,并且现在任何现有的复制表都将变为只读。
创建复制表
#
CREATE TABLE table_name
(
EventDate DateTime,
CounterID UInt32,
UserID UInt32,
Ver UInt16
)ENGINE = ReplicatedReplacingMergeTree(zoo_path, replica_name, other_parameters)
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
2021-03-16
起步
#
mysite/
manage.py #管理django项目的命令行工具
mysite/ #python包,名字就是当引用它内部任何东西时需要用到的python包名
__init__.py #告诉python这个目录应该被认为是一个python包
settings.py #项目的配置文件
urls.py #url声明
asgi.py #运行在asgi兼容的web服务器上的入口
wsgi.py #运行在wsgi兼容的web服务器上的入口
- 运行项目的命令
python manage.py runserver
- 创建一个应用
python manage.py startapp polls
- 应用urls.py中使用到的函数path()有四个参数,两个必须参数route和view,两个可选参数kwargs和name
- route是一个匹配URL的准则,当响应一个请求时,他会从urlpatterns的第一项开始,按顺序依次匹配列表中的项。这些准则不会匹配get和post参数或域名
- view:当找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个HttpRequest对象作为第一个参数,被捕获的参数以关键字参数的形式传入。
- kwages:任意个关键字参数可作为一个字典传递给目标视图函数。
- name:为url取名
- 函数include()允许引用其他URLconfs
模型
#
设置mysite/sittings.py
#
- 设置时区
TIME_ZONE = 'Asia/Shanghai' USE_TZ = True
- 当启用了时区支持(即
USE_TZ = True
),Django将在数据库中以UTC存储日期信息,而在模板和表单中转化为最终用户的时区
- 设置数据库
INSTALLED_APPS
是在项目中启用的Django应用,这里面的某些应用需要数据表,可以用python manage.py migrate
生成
创建模型
#
- 模型设真实数据的描述,它包含了存储的数据所必要的字段和行为。
- Django的迁移代码是从模型文件中自动生成的
- 每个模型都是
django.db.models.Model
类的子类,每个模型有许多的类变量,他们都表示一个数据库的字段,每个字段都是Field类的实例,每个Field类实例变量的名字也是数据库的列名。
#polls/models.py
from django.db import models
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
激活和修改模型
#
- 首先把应用安装到项目中,在
INSTALLED_APPS
中添加polls.apps.PollsConfig
- 执行
python manage.py makemigrations polls
- 迁移数据存放在应用的
migrations/0001_initial.py
中,可以通过python manage.py sqlmigrate polls 0001
来查看对应的sql
- 运行
python manage.py migtate
来应用数据库迁移
- 通过
python manage.py shell
可进入当前项目对应的命令行
- 给模型增加
__str__
方法能带来一些方便
Django管理页面
#
- 首先创建一个能登陆管理界面的用户
python manage.py createsuperuser
- 将创建的models注册,以便后台可以管理
#polls/admin.py
from django.contrib import admin
from .models import Question
admin.site.register(Question)
- 运行项目
python manage.py runserver
视图view
#
- 向应用的views.py中添加视图函数,这些函数的第一个参数是request,也以有第二、第三个参数。每个视图必须做的只有返回一个包含请求页面内容的HttpResponse对象或者抛出一个异常
def detail(request, question_id):
context = {
'question_id': question_id,
}
#template = loader.get_template('xxx/xxxx.html')
#return HttpResponse(template.render(context, request))
return render(request, 'xxx/xxxx.html', context)
#为url名称添加命名空间
app_name = 'polls'
urlpatterns = [
#...
#int:是一个转换器,决定了应该以什么变量类型匹配这部分url路径
path('<int:question_id>/detail/', views.detail, name='detail'),
]
- 当其他人访问网站的某个页面时,比如’polls/34/detail’,django将会载入项目的urls.py模块,因为这在ROOT_URLCONF中配置了,然后django寻找名为urlpatterns变量并按顺序匹配正则表达式,在找到匹配项’polls/’,它切掉了匹配的文本’polls/’,将剩余文本'34/detail’发送至’polls.urls’中做进一步处理
模板
#
- 在应用路径下创建templates目录,项目的TEMPLATES配置项描述了django如何载入和渲染模板,默认将APP_DIRS设置为True,这一选项让DjangoTemplates能在每个INSTALLED_APPS文件夹中寻找templates子目录。
- 由于django能直接访问到templates文件夹,为了放置出现模板文件名冲突,最好的方法就是将它们放到一个与自身应用重名的子文件夹里
- 指向具有命名空间的视图
<a href="{% url 'polls:detail' question.id %}">{{ context.question_id }}</a>
- 无论何时,当需要创建一个改变服务器端数据的表单时,请使用post。
- django自带了一个非常有用的防御系统,所有针对内部url的post表单都应该使用
{% csrf_token %}
模板标签。
- request.POST是一个字典类型对象,可以通过关键字来获取提交的数据,获取的值永远是字符串。如果key提供错误,将会抛出一个KeyError。
- 函数reverse()能避免在视图函数中硬编码url,他需要我们给出想要跳转的视图的名字和该视图所对应的url模式中需要给该视图提供的参数。
2021-03-15
\d
匹配一个数字
\w
匹配一个数字或字母
.
匹配任意字符
*
匹配0到多个字符,+
匹配1到多个字符,?
匹配0或1个字符,{n}
表示n个字符,{n,m}
表示n-m个字符
\s
匹配一个空格(也包括tab)
\d{3}\s+\d* #匹配3个数字,后面至少有一个空格,再后面有可能有数字,有可能没数字
[0-9a-z\_] #匹配一个数字或一个小写字母或一个下划线
A|B
可匹配A或B
^
表示行的开头,^\d
表示必须以数字开头
$
表示行的结束,$\d
表示必须以数字结束
re模块
#
- 因为python字符串本身也用
\
转义,可以使用r
前缀,就可以不用考虑字符串转义了。r'ABC\-001'
re.match(r'\d{3}\-\d$'),'010-1')
,如果匹配成功就返回一个Mache对象,否则返回none。
re.split(r'[\s\,]+','a,b, c d')
,切割字符串,能切割出['a','b','c']
- 正则表达式还有提取字串的功能,用
()
表示的就是要提取的分组。r'(\d{3})-(\d{3,8})'
就定义了两个组,可以使用group(n)
方法提取出子串
贪婪匹配
#
- 正则默认的时贪婪匹配,加个
?
就可以采用非贪婪匹配。\d+?
r = re.match('(\d+?)(0*)$','1200000')
print(r.groups())
#结果('12', '00000')
2021-03-15
datetime
#
from datetime import datetime,timedelta
- 获取当前时间
datetime.now()
- 指定的某个日期和时间
datetime(2021,3,15,19,48)
- 将datetime类型转化为timestamp,(timestamp是一个浮点数,整数位表示秒)
datetime(2021,3,15,19,49).timestamp()
- timestamp转化为datetime
datetime.fromtimestamp(1615809156.889315)
- str转换为datetime
datetime.strptime('2021-3-15 19:55:59','%Y-%m-%d %H:%M:%S')
- datetime转str
datetime.now().strftime('%a,%b %d %H:%M')
- datetime加减
datetime.now() + timedelta(days = 1, hours = 10)
- 时区转换
utc_dt = datetime.utcnow()
utc_dt.astimezone(timezone(timedelta(hours=8)))
collections
#
- namedtuple:是一个函数,可以用来创建一个自定义的tuple对象,同时规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
from collections import namedtuple
Point = nametuple('Point',['x', 'y'])
p = Point(1, 2)
- deque:双向列表,方法有
append()、pop()、appendleft()、popleft()
- defaultdict:和dict类似,但是在key不存在的时候返回一个默认值。
- OrderedDict:key会按照插入的顺序排序
- ChainMap:可以把一组dict串起来组成一个逻辑上的dict,本身也是一个dict,但在查找的时候,会按照顺序在内部的dict依次查找。
- Counter是一个计数器,可以统计字符出现的个数,实际上也是dict的子类
base64
#
- base64是一种用64个字符表示任意二进制数据的方法,64个字符指的是
A-Z, a-z, 0-9, +, /
。实现方法是将3个字节(3x8bit)的二进制数据编码为4字节(4x6bit,6bit的数据刚好能用上面这64个字符表示)的数据。6bit的数据刚好能用这64个字符表示。
- 编码
base64.b64encode(b'binary')
;解码base64.b64decode(b'binary')
hashlib
#
- 摘要算法又称为哈希算法,散列算法,它通过一个函数,把任意长度的数据转化为一个固定长度的数据串。它是一个单向函数,对原始数据做一点点的修改都会导致计算出的摘要完全不同。
import hashlib
md5 = hashlib.md5()
#也可以将串分开,多次调用update方法
md5.update("xiaoxiang.space".encode("utf-8"))
#md5结果是128bit,通常用32位的16进制表示
print(md5.hexdigest())
sha1 = hashlib.sha1()
sha1.update("xiaoxiang.space".encode("utf-8"))
#sha1的结果是160bit,通常用一个40位的16进制字符串表示
print(sha1.hexdigest())
- 无限迭代器
itertools.count()
- cycle()会把传入的一个序列无限重复下去
itertools.cycle('ABC')
- repeat()可以把一个元素无限重复下去,提供了第二个参数可以限定重复次数
itertools.repeat('A', 3)
- 可通过takewhile()根据函数条件来截取一个有限的序列。
- chain()可以把一组迭代对象串联起来,形成一个更大的迭代器。
itertools.chain('ABC', 'XYZ')
- groupby()可以把迭代器中相邻的重复元素挑出来放在一起。挑选规则是通过函数完成的,只要作用于函数的两个元素返回的值相等,这两个元素就被认为是一组的,函数的返回值作为组的key。
contextlib
#
- 任何对象,只要正确实现了上下文管理,就可以用于with语句。实现上下文管理是通过
__enter__
和__exit__
这两个方法实现的。可以通过contextlib标准库中的@contextmanager
来实现上下文管理(更简单)
- 有时候,我们希望在某段代码执行前后自动执行特定代码,也可以通过@contextmanager实现
import contextlib
@contextlib.contextmanager
def tag(name):
print("<%s>"%name)
yield
print("</%s>"%name)
with tag("h1"):
print("hello")
print("world")
- 如果一个对象没有实现上下文,就不能把他用于with语句,可以用closing()来将该对象变成上下文。
2021-03-15
错误
#
try:
i = 10 / int('a')
except ValueError as e:
print("ValueError",e)
except ZeroDivisionError as e:
print("ZeroDivisionError",e)
#raise如果不带参数,就会把当前错误原样抛出
raise
finally:
print("END")
单元测试要用到了再回来补充:
单元测试
IO
#
-
以读的方式打开一个文件对象f = open('test.txt','r')
-
文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源。可以使用with open('test.txt','r') as f:
,这种形式会自动帮我们调用close()方法。
-
读取二进制文件open('test','rb')
,读取非UTF-8编码的文件open('gbk.txt', 'r', encoding = 'gbk')
,遇到编码错误后忽略open('test', 'r', encoding = 'gbk', errors = 'ignore')
。
-
写文件就是将r
参数改为w
参数,当在写文件时,操作系统往往不会立刻把数据写入磁盘,而是先缓存起来,只有调用close方法时才会保证把没有写入的数据全部写入磁盘。
-
StringIO和BytesIO是在内存中读写数据。
-
序列化
2021-03-14
- python中所有数据类型都可视为对象。
- 所有的类都会继承object类。
- 类中定义函数的第一个参数是self,调用时不用传递该参数。
- 属性前面加
__
(两个下划线)就变成了一个私有属性。双下划线开头,双下划线结尾的是特殊变量,特殊变量是可以直接访问的,不是private变量。一个下划线开头的变量名是可以被外部访问的,但是按约定俗称的规定,请把它视为私有变量。
- 双下划线开头的属性不能被外部访问是因为这个属性被改名字了。
- 判断一个变量是否是某个类型可以用isinstance()判断,判断对象类型可以使用type方法。
- 使用dir()可获得对象的所有属性和方法。
- 自己写的类如果也想用len()方法的话,可以自己写一个
__len__
方法。实际上,当我们使用len()方法的时候,它会自动去调用该对象的__len__()
方法。
__str__
相当于java中的toString
,还有一个__repr__
是返回开发者看到的字符串。
__getitem__
可按下标访问数据的项。
- 任何类只要定义一个
__call__
方法,就可以对实例进行调用。
类的定义
#
class Student(object):
#类属性
name = 'Student'
def __init__(self, name):
#实例属性
self.name = name
#编写程序不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性
动态绑定
#
- 当创建了一个对象后,我们可以给该对象绑定任何属性和方法,但是该属性和方法只在当前对象上有效。为了给所有实例都绑定方法,可以给class绑定方法。
class Student(object):
#可以用来限制class实例能添加的属性,仅对当前类实例起作用,对继承的子类不起作用
#除非在子类中也定义__slots__,这样子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
__slots__ = ('name', 'age')
def set_age(self, age)
self.age = age
s = Student()
#给对象动态绑定了一个属性
s.name = 'xiaoxiang'
#给对象动态绑定一个方法,只在该对象上有效
s.set_age = MethodType(set_age, s)
#给类动态绑定一个方法
Student.set_age = set_age
getter和setter
#
- 使用@property代替getter,以及创建setter的方法见下面的代码
class Student:
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
student = Student()
student.name = 'xiaoxiang'
print(student.name)
__iter__
和__next__
#
- 如果一个类想被用于for-in,就必须实现
__iter__
和__next__
方法
class Fib:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
#实例自身就是迭代对象
return self
def __next__(self):
self.a, self.b = self.b, self.b + self.a
if self.a > 100:
raise StopIteration()
#返回下一个值
return self.a
for i in Fib():
print(i)
__getattr__
#
__getattr__
用于动态返回一个属性,只在没有找到属性的情况下才会调用该方法。
class Url:
def __init__(self, item = ''):
self.item = item
def __getattr__(self, item):
return Url('%s/%s'%(self.item, item))
def __str__(self):
return self.item
__repr__ = __str__
print(Url().xiaoxiang.ayu.liuchan.list)
#结果:/xiaoxiang/ayu/liuchan/list
枚举
#
from enum import Enum, unique
#该注解用于保证没有重复值
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
- class(类)的类型就是type,对象的类型就是类名
- 可以通过type()函数依次传入3个参数来创建一个class对象:①class的名称;②继承的父类集合;③class的方法名称与函数绑定。
type('hello', (object, ), dict(hello = 'hello world'))
- 通过type()函数创建的类和直接写class是完全一样的,因为python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
- 可以把类看作是metaclass(元类)创建出来的实例。
- metaclass后面用到了再来看吧,链接在这->
metaclass