这篇用来 记录一个 从零开始的 博客搭建,希望坚持下去,因为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. 商米D1S一体机设置搜狗手写输入法图解

    按照下图步骤,一步步设置即可,询问全新的时候需要点击允许. 商米应用市场搜索下载搜狗输入法,并安装 安装完成后,点击桌面搜狗输入法,选择启用搜狗输入法,如图 点击启用后,在虚拟键盘中选择搜狗输入法,并 ...

  2. MySQL下载与MySQL安装图解(MySQL5.7与MySQL8.0)

    MySQL下载与MySQL安装图解(MySQL5.7与MySQL8.0) 1.MySQL下载(MySQL8.0社区版) mysql下载方法,请根据风哥以下步骤与图示来下载mysql8.0最新社区版本: ...

  3. .Net Core 在Linux服务器下部署程序--(3). 部署.net core 后端程序

    确认第二步中的软件已安装完成 lrzsz文件上传下载软件 zip与unzip压缩包软件 net core 相关软件 确认上述软件安装完成之后,开始部署程序 创建部署文件夹 我的习惯是在usr文件夹下新 ...

  4. P4013 数字梯形问题 网络流

    题目描述 给定一个由 nn 行数字组成的数字梯形如下图所示. 梯形的第一行有 mm 个数字.从梯形的顶部的 mm 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径. 分别 ...

  5. idea 右键无java class选项

    项目中新建module之后,要在该目录下新增java Class文件,右键——>New发现无Java Class选项. File –Project Structure或者ctrl+alt+shi ...

  6. 网站升级HTTPS后WebSocket不能连接的问题

    一.前端代码 var socket = new WebSocket("wss://www.smcic.cn/wss/"); 注意点: 如果网站使用HTTPS,WebSocket必须 ...

  7. 随心测试_软测基础_007<软测学习路线建议>

    如果你对软测感兴趣,那么如何学习软件测试呢? 贴心小提示:以下内容,仅供参考,不挖坑 1:学习方式_职业教育选择观 ————SX的观点:成本 | 收益说,理性接受 软测产业服务链中,测试工程师重在于& ...

  8. Γ(a) 的两种方差与均值

    所以 这里是满足 Be(x+1,n-x+1),如果是要服从Be(a,b) 相应的后验概率 

  9. 小程序ios开发注意点

    两个月了啊,这两个月完成了一个vue的项目还有一个小程序,终于可以休息一下了, 今天先声明一个奇怪的bug,在我开发微信小程序的时候, 发现有个获取商品详情的接口在安卓手机上是可以获取数据的, 但是i ...

  10. 一入OI深似海 4 —— 纪念我最后一次PJ(中)

    不知道怎么回事,直到比赛前10分钟才放我们进考场. 考场在体育馆里面,很大很壮观. 我匆匆忙忙地找到位子,屁股还没坐热,被老师告知不能带水. what?! 于是我只好把水放在统一放私人物品的地方. 电 ...