这篇用来 记录一个 从零开始的 博客搭建,希望坚持下去,因为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. jqery autocomplete 动态传递参数的问题

    今天弄一个autocomplete 向后后台动态传递参数的问题 老的写法: params: { "saleid": $("#divSalesman input[field ...

  2. Windows中通过命令行启动打开Service 管理工具

    经常需要打开Services 管理工具操控Service 的启动,停止. 通过控制面板 --> 管理工具 -->Service  太慢. 学到一个快捷方式. windows + R  启动 ...

  3. DNS域名解析过程,域名的认识

    DNS域名解析过程 参考知乎:https://www.zhihu.com/question/23042131 当你通过浏览器输入url访问资源时,会请求DNS解析域名成对应的IP地址,由IP地址在去与 ...

  4. 015_python原生在线调试工具

    一.pdb https://docs.python.org/3/library/pdb.html

  5. django捡破烂

      一 Django的model form组件 这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来,先来一个简单的例子来看一下这个东西怎么用:比如我们的数据库 ...

  6. NodeJs之服务搭建与数据库连接

    NodeJs之服务搭建与数据库连接 一,介绍与需求分析 1.1,介绍 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻 ...

  7. Django(三) ORM 数据库操作

    大纲 一.DjangoORM 创建基本类型及生成数据库表结构 1.简介 2.创建数据库 表结构 二.Django ORM基本增删改查 1.表数据增删改查 2.表结构修改 三.Django ORM 字段 ...

  8. 1、Mysql无法创建外键的原因 2、MySql 外键约束 之CASCADE、SET NULL、RESTRICT、NO ACTION分析和作用

    在Mysql中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1.  两个字段的类型或者大小不严格匹配.例如,如果一个 ...

  9. java 枚举2

    package com.wsy.test; public enum Color { RED("红色",1),GREEN("绿色",2),BLUE("蓝 ...

  10. 拒绝回调,拥抱async await

    之前使用jquery中ajax,请求的结果需要写在回调函数里面,后面接触到了axios/fetch,使用了es6中Promise进行封装,这种链式结构调用,看起来比之前直观,可是还是没有解决回调的问题 ...