这篇用来 记录一个 从零开始的 博客搭建,希望坚持下去,因为python 开发效率令人发指,所以会原生从零写 ORM ,Web 框架

前提是打好 异步 io 的基础, 使用异步,有一点要谨记,一旦开始异步,层层异步,从 http 到 数据库层都要用异步框架写异步函数,所谓开弓没有回头箭

# -*- coding: utf-8 -*-
import asyncio
import time
from functools import wraps
__author__ = 'Frank Li' def time_count(func):
@wraps(func)
def inner_func(*args,**kw):
start = time.time()
result = func(*args,**kw)
end = time.time()
print('{} cost {:.1f} s...'.format(func.__name__,end-start))
return result
return inner_func async def do_some_work(n):
await asyncio.sleep(n) @asyncio.coroutine
def task_io_01():
print('{} start to run ...'.format(task_io_01.__name__))
n = 3
yield from do_some_work(n)
print('{} continue to work {} seconds later...'.format(task_io_01.__name__,n))
return task_io_01.__name__ @asyncio.coroutine
def task_io_02():
print('{} start to run ...'.format(task_io_02.__name__))
n = 5
yield from do_some_work(n)
print('{} continue to do the work in {} seconds'.format(task_io_02.__name__,n))
return task_io_02.__name__ @time_count
def main():
tasks = [task_io_02(),task_io_01()]
loop = asyncio.get_event_loop()
done, pending = loop.run_until_complete(asyncio.wait(tasks))
for d in done:
print('协程无序返回值:{}'.format(d.result()))
loop.close() if __name__ == '__main__':
main()

环境准备 flask 都不要,惊掉下巴

python 3.7

pip install aiohttp

pip install jinja2

pip install aiomysql

pycharm( python 开发 IDE)  + git bash(练习命令) + github(远程仓库)


在 pycharm 里 新建 project

并 cd 进入 project 目录

  1. 执行 git init 初始化本地仓库

  2. 登录 github 创建自己的远程仓库,因为没给钱是public 的仓库,放心,这点儿代码没人偷,而且python开发者首先信奉开源

  3. 执行命令 git remote add origin git@github.com:FrankLi99/awesome-python3-webapp.git 关联本地与远程仓库

  4. 创建如下图所示的 工程目录

  5. 添加 .gitignore 参考

  6. 执行 git add . , git commit -m "init commit" 添加到暂存区,并提交到本地仓库的 master 分支,因为就我一个人开发,其实 master + dev 两个分支就已足够

  7. 执行 git push -u origin master 推送本地仓库 master 分支 到远程 master 分支, -u 参数 还可以将 两个 master 分支 关联起来,下次拉取推送 就直接 git pull/push origin master

上面这么麻烦,完全可以使用 git clone repo-addr 来解决,只是 为了练习下另一种方法

day2 接下来 ,添加第一个 webserver --》 app.py

# -*- coding: utf-8 -*-
__author__ = 'Frank Li' import logging; logging.basicConfig(level=logging.INFO) import asyncio, os, json, time
from datetime import datetime from aiohttp import web def index(request):
return web.Response(body=b'<h1>Awesome</h1>',content_type='text/html') @asyncio.coroutine
def init(loop):
app = web.Application(loop=loop)
app.router.add_route('GET', '/', index)
srv = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9000)
logging.info('server started at http://127.0.0.1:9000...')
return srv loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

自此,我们 Web 骨架搭好了,可以进行深入开发了。。。

  1. 添加并提交到本地仓库 git add www/app.py
  2. 推送到远程仓库 git push origin master

day03 编写 orm 框架 ==> 使用 元类 + mysql 异步库 aiomysql 编写 orm 框架,看起来复杂,不过总要为之。

# 第一次写这么复杂的代码,我建议先把主要的 结构搭建出来,然后在熟读源码的基础上,再来给每个pass部分做替换(这部分真的很综合,我认为是进阶)

import asyncio
import logging;logging.basicConfig(level=logging.info)
import aiomysql def log(sql,args):
logging.info('SQL: {sql} , other args: {args}'.format(sql=sql,args=args)) @asyncio.coroutine
def create_pool(loop,**db_info):
logging.info('start to create aiomysql database connection pool...')
global __pool
__pool = yield from aiomysql.create_pool(host=db_info.get('host','localhost'),
port=db_info.get('port',3306),
db=db_info.get('db'),
user=db_info.get('user'),
password=db_info.get('password'),
charset=db_info.get('charset','utf-8'),
autocommit=db_info.get('autocommit',True),
minsize=db_info.get('minsize',1),
maxsize=db_info.get('maxsize',10),
loop=loop) @asyncio.coroutine
def select(sql,args,size=None):
log(sql,args)
global __pool
with (yield from __pool) as conn:
csr = yield from conn.cursor(aiomysql.DictCursor)
yield from csr.execute(sql.replace('?','%s'),args or ())
rs = csr.fetchmany(size) if size else csr.fetchall()
return rs @asyncio.coroutine
def execute(sql,args,autocommit=True):
log(sql,args)
global __pool
with (yield from __pool) as conn:
if not autocommit:
yield from conn.begin()
try:
csr = yield from conn.cursor()
yield from csr.execute(sql.replace('?','%s'),args or ())
affectedRow = csr.rowcount
if not autocommit:
yield from conn.commit()
except BaseException as e:
raise
if not autocommit:
yield from conn.rollback()
finally:
yield from csr.close()
return affectedRow # 根据长度构造 占位符
def create_args_string(num):
return ','.join('?'*num) # 定义 Field 字段类
class Field(object):
pass
class IntegerField(Field):
pass
class StringField(Field):
pass
class FloatField(Field):
pass
class BooleanField(Field):
pass
class TextField(Field):
pass # 定义实体类的元类,先有类来后有天,元类更在类之前
class ModelMetaclass(type):
pass class Model(dict,metaclass=ModelMetaclass):
pass

完善版 orm

# -*- coding: utf-8 -*-
__author__ = 'Frank Li' import asyncio
import logging;logging.basicConfig(level=logging.DEBUG)
import aiomysql def log(sql,args):
logging.info('SQL:{sql} , other ARGS:{args}'.format(sql=sql,args=args)) # 创建数据库连接池 -- 全局
@asyncio.coroutine
def create_pool(loop,**db_info):
logging.info('start to create global database connection pool...')
global __pool
__pool = yield from aiomysql.create_pool(host=db_info.get('host','localhost'),
port=db_info.get('port',3306),
db=db_info.get('db'),
user=db_info.get('user'),
password=db_info.get('password'),
charset=db_info.get('charset','utf8'),
autocommit=db_info.get('autocommit',True),
minsize=db_info.get('minsize',1),
maxsize=db_info.get('maxsize',10),
loop=loop) @asyncio.coroutine
def select(sql,args,size=None):
log(sql,args)
global __pool
with (yield from __pool) as conn:
csr = yield from conn.cursor(aiomysql.DictCursor)
yield from csr.execute(sql.replace('?','%s'),args or ())
rs = csr.fetchmany(size) if size else csr.fetchall()
yield from csr.close()
# logging.info('result rowcount: {}'.format(len(rs)))
return rs @asyncio.coroutine
def execute(sql,args,autocommit=True):
log(sql,args)
global __pool
with (yield from __pool) as conn:
if not autocommit:
yield from conn.begin()
try:
csr = yield from conn.cursor()
yield from csr.execute(sql.replace('?','%s'),args or ())
affctedRow = csr.rowcount
logging.info('execute sql affcted row: {}'.format(affctedRow))
if not autocommit:
yield from conn.commit() except BaseException:
if not autocommit:
yield from conn.rollback()
raise
finally:
try:
yield from csr.close()
except BaseException as e:
logging.info(e)
return affctedRow # 开始定义 字段类 Field
class Field(object):
def __init__(self,name,column_type,is_pk,default):
self.name = name
self.column_type = column_type
self.is_pk = is_pk
self.default = default
def __repr__(self):
return '<{},{}:{}>'.format(self.__class__.__name__,self.column_type,self.name)
__str__ = __repr__ class IntegerField(Field):
def __init__(self,name=None,column_type='bigint',is_pk=False,default=0):
super(IntegerField,self).__init__(name,column_type,is_pk,default) class StringField(Field):
def __init__(self,name=None,column_type='varchar(256)',is_pk=False,default=None):
super(StringField,self).__init__(name,column_type,is_pk,default) class FloatField(Field):
def __init__(self,name=None,column_type='real',is_pk=False,default=0.0):
super(FloatField,self).__init__(name,column_type,is_pk,default) class BooleanField(Field):
def __init__(self,name=None,column_type='boolean',default=False):
super(BooleanField,self).__init__(name,column_type,False,default) class TextField(Field):
def __init__(self,name=None,column_type='text',default=None):
super(TextField,self).__init__(name,column_type,False,default) # 根据参数个数构造 ? 占位符
def create_args_string(num):
return ','.join('?'*num) # 开始写 元类 ModelMetaclass 主要作用就是 对 所有 Model 构造 select ,insert,delete,update 语句
# 先有类来后有天,元类更在类之前
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
# 排除掉 Model 这个 父类本身
if name=='Model':
return type.__new__(cls,name,bases,attrs) tb_name = attrs.get('__table__',str.lower(name)) # 如果是其子类
mappings = {}
fields = []
primaryKey = None for k,v in attrs.items(): if isinstance(v,Field):
logging.info('found field mapping {} <==> {}'.format(k, v))
mappings[k] = v
if v.is_pk:
if primaryKey:
raise RuntimeError('Duplicated primary key for field: {}'.format(k))
primaryKey = k
else:
fields.append(k) if not primaryKey:
raise RuntimeError('Primary key not found...') # 去除 类中 与 实例中 变量同名的 类变量
for k in mappings.keys():
attrs.pop(k) escape_fields = list(map(lambda f: '`{}`'.format(f),fields)) # 将获取到的值 放入 类属性中
attrs['__mappings__'] = mappings
attrs['__table__'] = tb_name
attrs['__fields__'] = fields
attrs['__primary_key__'] = primaryKey # 构造 select , update ,delete ,insert 语句
attrs['__select__'] = 'select `{primaryKey}`,{escape_fields} from `{tb_name}` '.format(primaryKey=primaryKey,escape_fields=','.join(escape_fields),tb_name=tb_name)
attrs['__insert__'] = 'insert into `{tb_name}`({escape_fields},`{primaryKey}`) values({args})'.format(tb_name=tb_name,escape_fields=','.join(escape_fields),primaryKey=primaryKey,args=create_args_string(len(fields)+1))
attrs['__delete__'] = 'delete from `{tb_name}` where `{primaryKey}`=?'.format(tb_name=tb_name,primaryKey=primaryKey)
attrs['__update__'] = 'update `tb_name` set {set_cols} where `{primaryKey}`=?'.format(tb_name=tb_name,set_cols=','.join(list(map(lambda f:'`{}`=?'.format(mappings.get(f).name or f),fields))),primaryKey=primaryKey) # 返回 改造好的 类模板
return type.__new__(cls,name,bases,attrs) # 造实体类的 模板
class Model(dict,metaclass=ModelMetaclass): def __init__(self,*args,**kw):
super(Model,self).__init__(*args,**kw) # 方便 dict 对象 如同 属性一般调用 dict.name , dict.id 等等
def __getattr__(self,key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute {}".format(key))
def __setattr__(self,key,value):
self[key] = value def getValue(self,key):
return getattr(self,key,None) def getValueOrDefault(self,key):
value = self.getValue(key)
if value is None:
field = self.__mappings__[key]
if field is not None:
value = field.default() if callable(field.default) else field.default
logging.info('using default for {} : {}'.format(key,str(value)))
setattr(self,key,value)
return value @classmethod
@asyncio.coroutine
def findAll(cls,where=None,args=None,**kw):
'find object by where clause'
select_sql = [cls.__select__]
if where:
select_sql.append('where')
select_sql.append(where) if args is None:
args=[] orderBy = kw.get('orderbBy',None)
if orderBy:
select_sql.append('order by ')
select_sql.append(orderBy) limit = kw.get('limit',None)
if limit:
select_sql.append('limit')
if isinstance(limit,int):
select_sql.append('?')
args.append(limit)
elif isinstance(limit,tuple): select_sql.append(create_args_string(2)) # ?,?
args.extend(limit)
else:
raise ValueError('Invalid limit value: {}'.format(str(limit))) rs = yield from select(' '.join(select_sql),args)
return [cls(**r) for r in rs] @classmethod
@asyncio.coroutine
def findNumber(cls,selectField,where=None,args=None):
''' find number by select and where'''
select_sql = ['select {selectField} _num_ from `tb_name`'.format(selectField=selectField,tb_name=cls.__table__)]
if where:
select_sql.append('where')
select_sql.append(where)
rs = yield from select(' '.join(select_sql),args,1)
if len(rs) ==0:
return None
return rs[0]['_num_'] @classmethod
@asyncio.coroutine
def find(cls,pk):
'''find object by primary key'''
rs = yield from select('{select_sql} where `{pk_field}`=?'.format(select_sql=cls.__select__,pk_field=cls.__primary_key__),[pk],size=1)
return None if len(rs)==0 else cls(**rs[0]) # save
@asyncio.coroutine
def save(self):
args = list(map(self.getValueOrDefault,self.__fields__))
args.append(self.getValueOrDefault(self.__primary_key__))
rows = yield from execute(self.__insert__,args,True)
if rows != 1:
logging.warn('failed to insert record: affcted rows: {rowCount}'.format(rowCount=rows))
else:
logging.warn('affcted rows: {rowCount}'.format(rowCount=rows))
# update
@asyncio.coroutine
def update(self):
args = list(map(self.getValueOrDefault,self.__fields__))
args.append(self.getValueOrDefault(self.__primary_key__))
rows = yield from execute(self.__update__,args)
if rows !=1:
logging.warn('failed to update by primary key: affected {rowCount}'.format(rowCount=rows)) # delete
@asyncio.coroutine
def remove(self):
args = list(map(self.getValueOrDefault,self.__fields__))
args.append(self.getValueOrDefault(self.__primary_key__))
rows = yield from execute(self.__delete__,args)
if rows != 1:
logging.info('failed to delete by primary key affected rows: {rowCount}'.format(rowCount=rows))

model 模块

# -*- coding: utf-8 -*-
__author__ = 'Frank Li'
from www.orm2 import Model,StringField,IntegerField,BooleanField,FloatField,create_pool
import logging;logging.basicConfig(level=logging.DEBUG)
import time
from www.models import User
import asyncio
import uuid def next_id():
return '%015d%s000' % (int(time.time() * 1000), uuid.uuid4().hex) class User(Model):
__table__ = 'users' id = StringField(is_pk=True, default=next_id, column_type='varchar(50)')
email = StringField(column_type='varchar(50)')
passwd = StringField(column_type='varchar(50)')
admin = BooleanField()
name = StringField(column_type='varchar(50)')
image = StringField(column_type='varchar(500)')
created_at = FloatField(default=time.time) @asyncio.coroutine
def test(loop):
yield from create_pool(loop=loop,user='www-data', password='www-data', db='awesome')
u = User(name='Frank', email='Frank@example.com', passwd='1234567890', image='about:blank')
yield from u.save()
logging.info(u.__select__)
logging.info(u.__insert__)
logging.info(u.__delete__)
logging.info(u.__update__) users = yield from User.findAll()
for user in users:
logging.info('user info:\n '+str(user))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(test(loop))
loop.run_forever()

编写 异步 web 框架, 这里真的好难。。。比 ORM 还难很多,初次看来是这样,可能需要 细细钻研源码。。。

遍寻网络终于发现注释版的代码

这篇博客不错,终还是自己见识太浅薄

# 首先来个最简单的吧, 构造 两个 装饰器用来对应 前台页面传来的 两种请求 ,get / post
from functools import wraps,partial def handler_decorator(path,*,method='GET'):
def decorator(func):
@wraps(func) # 更正函数签名
def wrapper(*args,**kw):
result = func(*args,**kw)
return result
wrapper.__method__ = method
wrapper.__urlpath__ = path
return wrapper
return decorator get=partial(handler_decorator,method='GET')
post=partial(handler_decorator,method='POST') # 使用时候 就方便了
@get('/home/index')
def home_index(request):
pass

### 定义 RequestHandler 用来解析 request 中的 信息

import inspect,asyncio
from web_app.APIError import APIError
from aiohttp import web
from urllib import parse #运用inspect模块,创建几个函数用以获取URL处理函数与request参数之间的关系
def get_required_kw_args(fn): #收集没有默认值的命名关键字参数
args = []
params = inspect.signature(fn).parameters #inspect模块是用来分析模块,函数
for name, param in params.items():
if str(param.kind) == 'KEYWORD_ONLY' and param.default == inspect.Parameter.empty:
args.append(name)
return tuple(args) def get_named_kw_args(fn): #获取命名关键字参数
args = []
params = inspect.signature(fn).parameters
for name,param in params.items():
if str(param.kind) == 'KEYWORD_ONLY':
args.append(name)
return tuple(args) def has_named_kw_arg(fn): #判断有没有命名关键字参数
params = inspect.signature(fn).parameters
for name,param in params.items():
if str(param.kind) == 'KEYWORD_ONLY':
return True def has_var_kw_arg(fn): #判断有没有关键字参数
params = inspect.signature(fn).parameters
for name,param in params.items():
if str(param.kind) == 'VAR_KEYWORD':
return True def has_request_arg(fn): #判断是否含有名叫'request'参数,且该参数是否为最后一个参数
params = inspect.signature(fn).parameters
sig = inspect.signature(fn)
found = False
for name,param in params.items():
if name == 'request':
found = True
continue #跳出当前循环,进入下一个循环
if found and (str(param.kind) != 'VAR_POSITIONAL' and str(param.kind) != 'KEYWORD_ONLY' and str(param.kind != 'VAR_KEYWORD')):
raise ValueError('request parameter must be the last named parameter in function: %s%s'%(fn.__name__,str(sig)))
return found #定义RequestHandler,正式向request参数获取URL处理函数所需的参数,发现 请求信息有问题的 raise APIError ,没有问题 则放行 ,注意所有这些 fn 都是指的实际 handler.py 中的函数 class RequestHandler(object): def __init__(self,app,fn):#接受app参数
self._app = app
self._fn = fn
self._required_kw_args = get_required_kw_args(fn)
self._named_kw_args = get_named_kw_args(fn)
self._has_named_kw_arg = has_named_kw_arg(fn)
self._has_var_kw_arg = has_var_kw_arg(fn)
self._has_request_arg = has_request_arg(fn) async def __call__(self,request): #__call__这里要构造协程
kw = None
if self._has_named_kw_arg or self._has_var_kw_arg:
if request.method == 'POST': #判断客户端发来的方法是否为POST
if not request.content_type: #查询有没提交数据的格式(EncType)
return web.HTTPBadRequest(text='Missing Content_Type.')#这里被廖大坑了,要有text
ct = request.content_type.lower() #小写
if ct.startswith('application/json'): #startswith
params = await request.json() #Read request body decoded as json.
if not isinstance(params,dict):
return web.HTTPBadRequest(text='JSON body must be object.')
kw = params
elif ct.startswith('application/x-www-form-urlencoded') or ct.startswith('multipart/form-data'):
params = await request.post() # reads POST parameters from request body.If method is not POST, PUT, PATCH, TRACE or DELETE or content_type is not empty or application/x-www-form-urlencoded or multipart/form-data returns empty multidict.
kw = dict(**params)
else:
return web.HTTPBadRequest(text='Unsupported Content_Tpye: %s'%(request.content_type))
if request.method == 'GET':
qs = request.query_string #The query string in the URL
if qs:
kw = dict()
for k,v in parse.parse_qs(qs,True).items(): #Parse a query string given as a string argument.Data are returned as a dictionary. The dictionary keys are the unique query variable names and the values are lists of values for each name.
kw[k] = v[0]
if kw is None:
kw = dict(**request.match_info)
else:
if not self._has_var_kw_arg and self._named_kw_args: #当函数参数没有关键字参数时,移去request除命名关键字参数所有的参数信息
copy = dict()
for name in self._named_kw_args:
if name in kw:
copy[name] = kw[name]
kw = copy
for k,v in request.match_info.items(): #检查命名关键参数
if k in kw:
logging.warning('Duplicate arg name in named arg and kw args: %s' % k)
kw[k] = v
if self._has_request_arg:
kw['request'] = request
if self._required_kw_args: #假如命名关键字参数(没有附加默认值),request没有提供相应的数值,报错
for name in self._required_kw_args:
if name not in kw:
return web.HTTPBadRequest(text='Missing argument: %s'%(name))
logging.info('call with args: %s' % str(kw)) try:
r = await self._fn(**kw)
return r
except APIError as e: #APIError另外创建
return dict(error=e.error, data=e.data, message=e.message)
# 注册 url  对应处理函数 相当于 spring中的  @RequestMapping  注解

import inspect,asyncio

#编写一个add_route函数,用来注册一个URL处理函数
def add_route(app,fn):
method = getattr(fn,'__method__',None)
path = getattr(fn,'__route__',None)
if method is None or path is None:
return ValueError('@get or @post not defined in %s.'%str(fn))
if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn): #判断是否为协程且生成器,不是使用isinstance
fn = asyncio.coroutine(fn)
logging.info('add route %s %s => %s(%s)'%(method,path,fn.__name__,','.join(inspect.signature(fn).parameters.keys())))
app.router.add_route(method,path,RequestHandler(app,fn))#别忘了RequestHandler的参数有两个

# 上面注册如果 一个一个 弄 岂不是会烦死,下面来一个 模块导入,批量注册
#直接导入文件,批量注册一个URL处理函数
def add_routes(app,module_name):
n = module_name.rfind('.')
if n == -1:
mod = __import__(module_name,globals(),locals())
else:
name = module_name[n+1:]
mod = getattr(__import__(module_name[:n],globals(),locals(),[name],0),name)#第一个参数为文件路径参数,不能掺夹函数名,类名
for attr in dir(mod):
if attr.startswith('_'):
continue
fn = getattr(mod,attr)
if callable(fn):
method = getattr(fn,'__method__',None)
path = getattr(fn,'__route__',None)
if path and method: #这里要查询path以及method是否存在而不是等待add_route函数查询,因为那里错误就要报错了
### 说到底 还是要 顺着 aiohttp 提供的 接口来,给他 构造出他要的参数
import os,logging #添加静态文件夹的路径
def add_static(add):
path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'static')#输出当前文件夹中'static'的路径
app.router.add_static('/static/',path)#prefix (str) – URL path prefix for handled static files
logging.info('add static %s => %s'%('/static/',path))
from jinja2 import Environment, FileSystemLoader
from datetime import datetime
import json, time
import logging #初始化jinja2,以便其他函数使用jinja2模板
def init_jinja2(app, **kw):
logging.info('init jinja2...')
options = dict(
autoescape = kw.get('autoescape', True),
block_start_string = kw.get('block_start_string', '{%'),
block_end_string = kw.get('block_end_string', '%}'),
variable_start_string = kw.get('variable_start_string', '{{'),
variable_end_string = kw.get('variable_end_string', '}}'),
auto_reload = kw.get('auto_reload', True)
)
path = kw.get('path', None)
if path is None:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
logging.info('set jinja2 template path: %s' % path)
env = Environment(loader=FileSystemLoader(path), **options)
filters = kw.get('filters', None)
if filters is not None:
for name, f in filters.items():
env.filters[name] = f
app['__templating__'] = env def datetime_filter(t):
delta = int(time.time() - t)
if delta < 60:
return u'1分钟前'
if delta < 3600:
return u'%s分钟前' % (delta // 60)
if delta < 86400:
return u'%s小时前' % (delta // 3600)
if delta < 604800:
return u'%s天前' % (delta // 86400)
dt = datetime.fromtimestamp(t)
return u'%s年%s月%s日' % (dt.year, dt.month, dt.day)

使用原生 python 造轮子搭建博客的更多相关文章

  1. Python课程设计 搭建博客

    安装包Github地址 Python综合设计 233博客 注意还有个email文件是需要填入自己信息的,比如最高权限账号和要发送邮件的账号密码 请安装Python2.7环境,本服务器所用环境为 设置环 ...

  2. Python爬取CSDN博客文章

    0 url :http://blog.csdn.net/youyou1543724847/article/details/52818339Redis一点基础的东西目录 1.基础底层数据结构 2.win ...

  3. python实现文章或博客的自动摘要(附java版开源项目)

    python实现文章或博客的自动摘要(附java版开源项目) 写博客的时候,都习惯给文章加入一个简介.现在可以自动完成了!TF-IDF与余弦相似性的应用(三):自动摘要 - 阮一峰的网络日志http: ...

  4. Django练习项目之搭建博客

    背景:自从今年回家过年后,来到公司给我转了试用,我的学习效率感觉不如从前,而且刚步入社会我总是想要怎么想明白想清楚一些事,这通常会花掉,消耗我大量的精力,因为我想把我的生活管理规划好了,而在it技术学 ...

  5. flask tutorial => make a blog :) flask 搭建博客系统从零开始!

    please follow the tutorial from the official site :) http://flask.pocoo.org/docs/ You could download ...

  6. 基于Hexo搭建博客并部署到Github Pages

    基于Hexo搭建博客并部署到Github Pages 之前在简书上写东西,觉得自己还是太浮躁.本来打算用Flask自己写一个,以为是微框架就比较简单,naive.HTML.CSS.JS等都要学啊,我几 ...

  7. Django搭建博客网站(四)

    Django搭建博客网站(四) 最后一篇主要讲讲在后台文章编辑加入markdown,已经在文章详情页对markdown的解析. Django搭建博客网站(一) Django搭建博客网站(二) Djan ...

  8. Django搭建博客网站(三)

    Django搭建博客网站(三) 第三篇主要记录view层的逻辑和template. Django搭建博客网站(一) Django搭建博客网站(二) 结构 网站结构决定我要实现什么view. 我主要要用 ...

  9. Django搭建博客网站(二)

    Django搭建自己的博客网站(二) 这里主要讲构建系统数据库Model. Django搭建博客网站(一) model 目前就只提供一个文章model和一个文章分类标签model,在post/mode ...

随机推荐

  1. Python3 字典 items() 方法

    描述 Python 字典 items() 方法以列表返回可遍历的(键, 值) 元组数组. 语法 items()方法语法: dict.items() 参数 NA. 返回值 返回可遍历的(键, 值) 元组 ...

  2. sqlsever存储过程配合代理作业自动定时建表

    1.自动建表存储过程 USE [ThreeToOne] GO /****** Object:  StoredProcedure [dbo].[WTO_CreateTable_ScanDoXXX]    ...

  3. k8s部署dashboard:v1.5.1

    1.准备dashboard.yaml文件 apiVersion: extensions/v1beta1 kind: Deployment metadata: # Keep the name in sy ...

  4. 英语-TOEFL和GRE复习计划与资料

    目录 一. TOEFL (1). 阅读: 60 minutes (2). 听力: 50 minutes (3). 口语: 20 minutes (4). 作文: 60 minutes 单词准备 其他资 ...

  5. 简述同步IO、异步IO、阻塞IO、非阻塞IO之间的联系与区别

    POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. ...

  6. 浅谈css3有意思的属性pointer-events: none;

    pointer-events: none: 可以让某个元素实现类似于海市蜃楼的效果,具体理解为,你可以看的到某个元素,但是你无法摸的着.   而display:none; 是你摸不着,但是你也看不见. ...

  7. Windows API方式直接调用C#的DLL,支持多音字转拼音、Gzip解压缩、公式计算(VBA、C++、VB、Delphi甚至java都可以)

    原始链接 https://www.cnblogs.com/Charltsing/p/DllExport.html 这两年,我在VBA应用方面一直有几大痛点:1.多音字转拼音:2.64位下的GZIP解压 ...

  8. Python开发【第十六篇】:AJAX全套(转)

    作者:武沛齐 出处:http://www.cnblogs.com/wupeiqi/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接. 概述 对于 ...

  9. 安装 VMware CentOS Xmanager Xshell

    1.CentOS下载CentOS是免费版,推荐在官网上直接下载,网址:https://www.centos.org/download/DVD ISO:普通光盘完整安装版镜像,可离线安装到计算机硬盘上, ...

  10. npm link & unlink

    npm link & unlink https://dev.to/erinbush/npm-linking-and-unlinking-2h1g