leiem.cn
Open in
urlscan Pro
163.181.145.171
Public Scan
URL:
https://leiem.cn/
Submission: On January 05 via api from US — Scanned from US
Submission: On January 05 via api from US — Scanned from US
Form analysis
1 forms found in the DOMGET //google.com/search
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="搜索"><button type="submit" class="search-form-submit"></button><input type="hidden"
name="sitesearch" value="https://leiem.cn"></form>
Text Content
首页 文章 2023-12-03 前端 EVENTSOURCE入门 EventSource 接口是 web 内容与服务器发送事件通信的接口。一个 EventSource 实例会对 HTTP 服务器开启一个持久化的连接,以 text/event-stream 格式发送事件。 与 WebSocket 不同的是,服务器发送事件是单向的。数据消息只能从服务端到发送到客户端(如用户的浏览器)。这使其成为不需要从客户端往服务器发送消息的情况下的最佳选择。 前端示例代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let source = new EventSource('/api/hello') // 连接建立完成 source.onopen = function () { console.log('Connection was opened.'); } // 收到消息,event:message source.onmessage = function (event) { console.log('on message: ', event.data); } // 连接失败/关闭 source.onerror = function (event) { console.log('on error: ', event) // 可以手动 close(),禁止默认的自动重连机制 source.close() } // 指定 event 的处理,event: ping source.addEventListener('ping', function (event) { console.log('ping: ', event.data); }) Python代码,Django 示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from django.http import StreamingHttpResponse import json def hello(request): # 生成器函数 def handle_data(): for i in range(int(last_id or -1) + 1, 100): # 触发前端的 onmessage 回调函数,默认 event:message yield f'data:data is {i}\n\n' # 返回复杂数据(转JSON字符串) data = {'a': 1, 'b': 2} yield f'data:{json.dumps(data)}\n\n' # 触发前端的 addEventListener('ping'),event:ping yield f'event:ping\ndata: ping data...\n\n' response = StreamingHttpResponse(handle_data(), content_type='text/event-stream') response['Cache-Control'] = 'no-cache' return response 分享 * javascript 2023-08-20 后端 PYTHON LOGGING入门 Python 内置的日志记录工具 配置 * Level 日志级别 * DEBUG * INFO * WARNING * ERROR * CRITICAL 基本使用 1 2 3 4 5 6 import logging # 日志记录到./demo.log文件中,format指定日志格式 logging.basicConfig(filename='./demo.log', format='%(asctime)s %(message)s') logging.warning('hello') 格式化 1 2 3 4 5 # 基础使用 logging.basicConfig(filename='./demo.log', format='%(asctime)s %(message)s') # 记录器使用,配合Handler formatter = logging.Formatter('%(asctime)s %(message)s') handler.setFormatter(formaater) 格式 描述 %(asctime)s 表示人类易读的 LogRecord 生成时间。 默认形式为 ‘2003-07-08 16:49:45,896’ (逗号之后的数字为时间的毫秒部分)。 %(created)f LogRecord 被创建的时间(即 time.time() 的返回值)。 %(filename)s pathname 的文件名部分。 %(funcName)s 函数名包括调用日志记录. %(levelname)s 消息文本记录级别('DEBUG','INFO','WARNING','ERROR','CRITICAL')。 %(levelno)s 消息数字的记录级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL). %(lineno)d 发出日志记录调用所在的源行号(如果可用)。 %(message)s 记入日志的消息,即 msg % args 的结果。 这是在发起调用 Formatter.format() 时设置的。 %(module)s 模块 (filename 的名称部分)。 %(msecs)d LogRecord 被创建的时间的毫秒部分。 %(name)s 用于记录调用的日志记录器名称。 %(pathname)s 发出日志记录调用的源文件的完整路径名(如果可用)。 %(process)d 进程ID(如果可用) %(processName)s 进程名(如果可用) %(relativeCreated)d 以毫秒数表示的 LogRecord 被创建的时间,即相对于 logging 模块被加载时间的差值。 %(thread)d 线程ID(如果可用) %(threadName)s 线程名(如果可用) 文件日志 1 2 3 4 5 6 7 8 9 10 11 12 13 import logging # 创建记录器对象 logger = logging.getLogger('log1') # 创建文件处理器,指定日志文件 handler = logging.FileHandler('./demo.log') # 格式器 formatter = logging.Formatter('%(asctime)s %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel('INFO') logger.error('hello error') 轮换日志(基于文件大小) 1 2 3 4 5 6 7 8 9 10 from logging.handlers import RotatingFileHandler import logging logger = logging.getLogger('log1') # maxBytes 设置单个文件的最大值(单位:字节) # backupCount 设置最多保留多少个备份文件 # maxBytes 和 backupCount 都必须同时设置才生效 # 以下配置会最多生成 demo.log demo.log.1 demo.log2 demo.log3 4个文件 handler = RotatingFileHandler('./demo.log', maxBytes=1024, backupCount=3) logger.addHandler(handler) 轮换日志(基于日期) 1 2 3 4 5 6 7 8 9 from logging.handlers import TimedRotatingFileHandler import logging logger = logging.getLogger('log1') # when 设置轮换间隔时间(midnight:午夜0点) # backupCount 设置最多保留多少个备份文件 # 以下配置会每天生成新文件最多保留7天历史文件 handler = TimedRotatingFileHandler('./demo.log', when='midnight', backupCount=7) logger.addHandler(handler) 分享 * Python 2023-05-07 python DJANGO ORM入门 表结构如下 1 2 3 4 5 6 7 8 9 10 11 from django.db import models class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() remarks = models.CharField(max_length=255, null=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'persons' 基础操作 filter 与 exclude用法一致 * 插入 1 2 3 4 5 6 7 # 方式一 Person.objects.create(name='张三', age=22) # 方式二 person = Person(name='李四', age=23) person.save() # 方式三,查询匹配到更新,否则插入 Person.objects.update_or_create(name='张三三', defaults={'age': 100}) * 更新 1 Person.objects.filter(name='张三三').update(age=F('age') + 1, remarks='我是备注') * 删除 1 2 3 4 5 # 匹配删除 Person.objects.filter(age=23).delete() # 单个删除 person = Person.objects.get(id=1) person.delete() * 简单查询 1 2 3 4 5 6 7 8 # 简单查询, 如果未匹配到或匹配到多条则报错 Person.objects.get(id=1) # 查询第一条数据 Person.objects.first() # 查询最后一条数据 Person.objects.last() # 查询所有数据 Person.objects.all() * 条件查询 1 2 3 4 5 # 根据指定字段匹配查询 Person.objects.filter(name='10号').first() # 多条件匹配 Person.objects.filter(age=30, name__contains='张') # 都满足条件 Person.objects.filter(Q(age=22) | Q(name__contains='张')) # 满足任意一个 * 大小比较 1 2 3 4 5 6 7 8 # 大于 Person.objects.filter(age__gt=40) # 大于等于 Person.objects.filter(age__gte=40) # 小于 Person.objects.filter(age__lt=40) # 小于等于 Person.objects.filter(age__lte=40) * 包含 1 2 3 4 5 6 7 8 9 10 # 年龄是23, 24, 25的 Person.objects.filter(age__in=(23, 24, 25)) # 名字包含字符串‘张’的 Person.objects.filter(name__contains='张') # 名字以‘张’开头 Person.objects.filter(name__startswith='张') # 名字以‘张’结尾 Person.objects.filter(name__endswith='张') # 匹配时忽略大小写,icontains/istartswith/iendswith Person.objects.filter(name__istartswith='a') * 日期/时间 1 2 3 4 5 6 7 8 9 10 # 匹配年 Person.objects.filter(created_at__year=2022) # 年/月/日/周 __month __day __week_day __week __hour __minute __second * 其他 1 2 3 4 # 过滤字段是否为NULL Person.objects.filter(remarks__isnull=True) # 正则匹配,iregex 忽略大小写 Person.objects.filter(name__regex=r'[0-9]+') 关联查询 表结构信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from django.db import models class Class(models.Model): name = models.CharField(max_length=30) remarks = models.CharField(max_length=255, null=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'classes' class Person(models.Model): cls = models.ForeignKey(Class, on_delete=models.PROTECT, null=True) name = models.CharField(max_length=30) age = models.IntegerField() remarks = models.CharField(max_length=255, null=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'persons' * 关联查询 1 2 3 4 5 6 7 # 通过人查询所属班级信息 person = Person.objects.first() print(person.cls.name) # 查询班级下的学生 cls = Class.objects.filter(name='一一班').first() cls.person_set.all() # 班级里的所有人 cls.person_set.filter(age__in=(22, 23)) # 对属于版本的学生再匹配过滤 * 关联匹配 1 2 3 4 # 匹配属于一一班的学生 Person.objects.filter(cls__name='一一班') # 匹配一年级的学生(班级名称一开头) Person.objects.filter(cls__name__startswith='一') * 查询优化 1 2 3 4 # 关联查询避免访问学生的班级信息时再次查询班级表 Person.objects.select_related('cls') # sql层面 Person.objects.prefetch_related('cls') Person.objects.annotate(cls_name=F('cls__name')) # 仅关联指定字段查询 * 其他 * 参考另外一篇文章 https://www.leiem.cn/2022/02/02/27221/ * 参考官网文档 https://docs.djangoproject.com/en/3.2/ref/models/relations/ 分享 * ORM * django 2023-04-02 后端 HTTP请求参数解析器实现 HTTP请求参数 * GET / DELETE 查询参数/URL参数 * https://www.baidu.com/?key=123 * request.GET = {“key”: “123”} * POST / PATCH / PUT * 请求体(body)application/json www-xxxx form-data * request.POST {“key”: “123”} 解析器实现目标 * 能解析GET/POST参数 * 参数校验 * 参数类型,例如必需是int * 必填判断 * 自定义校验,例如该参数只是能(“a”, “b”, “c”)中的一个 代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 import json body = { "id": "12", "url": "https://gitee.com/yooke/User.git", "type": "c", } # request.GET # request.POST # application/json json.loads(request.body) body = json.dumps(body) class Argument: def __init__(self, key, required=True, filter=None, type=None, help=None): self.key = key self.required = required self.filter = filter self.type = type self.help = help class Parser: def __init__(self, *arguments): self.arguments = arguments def parse(self, data): form, error = {}, None for arg in self.arguments: value = data.get(arg.key) if arg.required and value is None: # 判断required属性是否必填 error = arg.help break if arg.type: # 判断type属性类型是否正确 if not isinstance(value, arg.type): try: value = arg.type(value) # 尝试转换类型 except ValueError: error = arg.help break if arg.filter and not arg.filter(value): # 判断是否符合filter过滤条件 error = arg.help break form[arg.key] = value return form, error class JSONParser(Parser): # 扩展解析JSON消息体 def parse(self, data): data = json.loads(data) return super().parse(data) class XMLParser(Parser): # 扩展解析XML消息体 def xmltodict(self, data): # TODO: xml to dict return data def parse(self, data): data = self.xmltodict(data) return super().parse(data) def main(): form, error = JSONParser( Argument('id', type=int, help='ID必须是整数'), Argument('name', required=False, help='name参数必填'), Argument('url', help='url参数必填'), Argument('type', filter=lambda x: x in ('a', 'b', 'c'), help='type参数必须是a,b,c中的一个'), ).parse(body) if error is None: print('参数校验通过: ', form) else: print('参数校验失败: ', error) main() 分享 * Python 2023-03-13 随记 不同路径写法对RSYNC的影响 Rsync 同步时需要指定源路径与目标路径,那么路径末尾的 / 会影响同步的结果吗?做了以下测试 同步目录 以源路径 /Downloads/User 目录,远端目录 /data 为例 * rsync Dowloads/User root@ip:/data * /data 存在,/data/User 与源目录一致 * /data不存在,/data/User与源目录一致 * rsync Downloads/User root@ip:/data/ * /data存在,/data/User与源目录一致 * /data不存在,/data/User与源目录一致 * rsync Downloads/User/ root@ip:/data * /data存在,/data 与源目录一致 * /data不存在,/data与源目录一致 * rsync Dowloads/User/ root@ip:/data/ * /data存在,/data与源目录一致 * /data 不存在, /data与源目录一致 同步文件 以源路径 /Downloads/User/a.txt文件,远端路径 /data 为例 * rsync Dowloads/User/a.txt root@ip:/data * /data 存在,/data 覆盖内容与a.txt一致 * /data不存在,/data 创建文件,内容与a.txt一致 * rsync Downloads/User/a.txt root@ip:/data/ * /data存在,/data/a.txt 与a.txt一致 * /data不存在,/data/a.txt 与a.txt一致 * rsync Downloads/a.txt/ root@ip:/data * /data存在,报错:”Downloads/User/a.txt/.” failed: Not a directory (20) * /data不存在,报错:”Downloads/User/a.txt/.” failed: Not a directory (20) * rsync Dowloads/User/a.txt/ root@ip:/data/ * /data存在,报错:”Downloads/User/a.txt/.” failed: Not a directory (20) * /data 不存在,报错:”Downloads/User/a.txt/.” failed: Not a directory (20) 总结 * 同步的源路径为目录时 * 源路径以/结尾时,同步源路径下边的所有文件至目标路径内 * 源路径非/结尾时,同步源路径自身至目标路径内(源目录会作为目标路径的子目录) * 与目标路径是否以/结尾无关 * 同步的源路径为文件时 * 源路径以/结尾时报错,无法执行同步 * 目标路径以/结尾时,同步文件至目标路径下,新建或覆盖目标路径下的同名文件 * 目标路径非/结尾时,目标路径即为同步之后的文件路径(可理解为把源文件重命名为目标文件) 分享 * rsync 2023-03-05 随记 XTERMJS使用入门 Xterm是一个实现web终端的js库。 使用方法 1. 安装依赖 1 2 npm install xterm yarn add xterm 2. 引入xterm 1 import { Terminal } from 'xterm' 3. 相关的html代码 1 <div id="terminal"/> 4. 相关的js代码 1 2 3 let term = new Terminal() term.open(document.getElementById('terminal')); term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ') 常用配置 * 字体 * term.options.fontFamily = 'monospace' * 字号 * term.options.fontSize = 12 * 行号 * term.options.lineHeight = 1.2 * 主题配色 * term.options.theme = {background: '#2b2b2b', foreground: '#A9B7C6', cursor: '#2b2b2b'} 常用插件 * xterm-addon-fit 提供terminal内容自适应 1 2 3 4 5 import { FitAddon } from 'xterm-addon-fit' const fitAddon = new FitAddon(); term.loadAddon(fitAddon); fitAddon.fit() 分享 2023-02-19 Python PYTHON使用OPENPYXL读写EXCEL * 读取excel的内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 wb = load_workbook('/Users/aka/Downloads/w_pyxl.xlsx') ws = wb['Sheet1'] for row in ws.rows: # 遍历所有行 # 按索引取出每行的指定位置的值 print(row[0].value, row[1].value, row[2].value, row[3].value) # 每行组合成字典返回 def parse_ws(sheet): keys = [] for index, row in enumerate(sheet.rows): if index == 0: for item in row: keys.append(item.value) continue values = [x.value for x in row] yield dict(zip(keys, values)) wb = load_workbook('/Users/aka/Downloads/w_pyxl.xlsx') ws = wb['Sheet1'] for line in parse_ws(ws): print('姓名:', line['姓名']) # 通过表头来取值 * 生成excel文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from openpyxl import Workbook wb = Workbook() ws = wb.active data = [ ('张三', '一三班', '100'), ('李四', '一三班', '65'), ('王武', '一三班', '95') ] ws.append(('姓名', '班级', '分数')) for item in data: ws.append(item) wb.save('/Users/aka/Downloads/test.xlsx') 分享 * excel * openpyxl 2022-07-11 docker 使用BUILDX创建多架构镜像 > 参考文档1:https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images > > 参考文档2:https://docs.docker.com/desktop/multi-arch/ 需求 * 内核版本 >= 4.8 * 通过 https://docs.docker.com/engine/install/ 安装docker,或参考 https://docs.docker.com/build/buildx/install/ 安装 buildx 插件 步骤 1. 启用 BINFMT_MISC 1 docker run --privileged --rm tonistiigi/binfmt --install all 2. 创建并切换构建器 1 2 3 docker buildx create --name mybuilder docker buildx use mybuilder docker buildx inspect --bootstrap 3. 构建镜像 1 docker buildx build --platform linux/amd64,linux/arm64 -t openspug/spug-service --push . 分享 * docker 2022-05-11 前端 FLEX布局元素被挤压 问题 先看个常见的需求,元素A 设置了固定宽度 100px,但当元素B内容过多时会挤压A的宽度。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <style> .container { display: flex; flex-direction: row; width: 200px; border: 1px solid #999; } .a { width: 100px; } </style> <div class="container"> <div class="a">leiem.cn</div> <div>先看个常见的需求,元素A 设置了固定宽度 `100px`,但当元素B内容过多时会挤压A的宽度。</div> </div> image-20220511122609088 可以看到总宽度 200px 元素A 设置的宽度 100px 应该占据一半的空间,但展示的效果明显被挤压了。 解决方法 处理办法也很简单就是 flex-shrink 属性,该属性定义了当父元素主轴空间不足时子元素的缩小比例。具体怎么缩小还受其他属性的影响,我们这里就不展开详述了,因为大部分情况下也不会遇到那么复杂场景。 针对上述例子只需要在元素A上添加 flex-shrink: 0 即可解决。 1 2 3 4 .a { width: 100px; flex-shrink: 0; } 添加后的效果,完美解决 😄 image-20220511123334535 分享 * css 2022-04-18 python DJANGO ORM小记 对一些常用的小技巧记录。 SELECT_FOR_UPDATE select_for_update 可以做小并发的控制,其只能在事务中使用。符合条件的查询结果会被加锁,其他查询包含加锁的数据时会阻塞,直到事务完成,如果其他查询结果不包含加锁的记录则不受影响。 1 2 3 4 5 6 7 from django.db import transaction with transaction.atomic(): # 会给所有type = '1' 的记录加锁 tickets = Ticket.objects.select_for_update().filter(type='1') # 单条记录加锁 ticket = Ticket.objects.select_for_update().get(id=12) SELECT_RELATED select_related 在查询时通过外键关系将关联的对象一起查询出来,从而避免查询查询以优化查询性能,适合一对一/一对多关系或关联表数据量比较小的情况。 1 Person.objects.select_related('cls') 对应执行的sql语句 1 SELECT `persons`.`id`, `persons`.`name`, `classes`.`id`, `classes`.`name` ... FROM `persons` LEFT OUTER JOIN `classes` ON (`persons`.`cls_id` = `classes`.`id`) PREFETCH_RELATED prefetch_related 一样可以用于优化查询性能,但实现方式与select_related不同,其会产生两条sql语句,查询结果会被缓存到内存中,然后进行合并,适合对多关系或关联表数据量比较大的情况。 1 Person.objects.prefetch_related('cls') 对应执行的两条sql语句 1 2 SELECT `persons`.`id`, `persons`.`name` ... FROM `persons`; SELECT `classes`.`id`, `classes`.`name` ... FROM `classes` WHERE `classes`.`id` IN (2, 3)'; 分享 * ORM * django 12下一页 » 分类 * Python * docker * python * 前端 * 后端 * 小程序 * 随记 标签 * ORM * Python * antd * css * django * docker * excel * javascript * openpyxl * react * rsync 标签云 ORM Python antd css django docker excel javascript openpyxl react rsync 归档 * 十二月 2023 * 八月 2023 * 五月 2023 * 四月 2023 * 三月 2023 * 二月 2023 * 七月 2022 * 五月 2022 * 四月 2022 * 三月 2022 * 二月 2022 * 一月 2022 最新文章 * EventSource入门 * Python logging入门 * Django ORM入门 * HTTP请求参数解析器实现 * 不同路径写法对Rsync的影响 * Xtermjs使用入门 * Python使用openpyxl读写excel * 使用buildx创建多架构镜像 * flex布局元素被挤压 * Django ORM小记 豫ICP备16000449号-2 © 2023 leiem.cn 首页 文章