Coding
Django
dirtree
mysite :pycharm默认的项目根目录,import会正常导入下面的各个目录.
- db.sqlite3 :对应了models.py.数据库中的表名如:app1_usertype..
- manage.py :python manage.py startapp app1
- mysite
- __init__.py :python3可以不需要.
- settings.py :数据库,指定html路径,指定添加的app..
- urls.py :route(rul->method)
- wsgi.py :一般会替换为uWSGI+NGINX,性能更好一些.
- templetes :html
- master.html :属于公共的html模板,实现了大部分公共界面,里面预留了一些block,等待他业务html继承.
- static :js,images,video..
- app1
- templetestags
- func.py :里面定义一些函数共html中使用
- migrations :记录数据库的每次修改
- admin.py :后台管理
- apps.py :对当前app1的配置
- models.py :每个类都对应了数据中的一张表!
- views.py :业务处理,回调函数对应于urls.py.
- tests.py :单元测试
- middle
m1.py :中间件,对应于settings.py-MIDDLEWARE.
python manage.py createsuperuser # 建立超级管理员,对应admin.py!
python manage.py makemigrations # 修改model后同步到数据库
python manage.py migrate # 应用上面的更改
settings.py
INSTALLED_APPS = (
....
'app1', # 不然models的类不能配合makemigrations同步到数据库!
)
DATABASES = (
'default': {
# django默认使用mysqldb连接,故需要在mysite/__init__.py中需要'import pymysql && pymysql.install_as_MySQLdb()'
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbname', # 必须是已经创建好的数据库
'USER': 'root',
'PASSWORD': 'pwd',
'HOST': '',
'PORT': '',
}
)
STATICFILES_DIR = (
os.path.join(BASE_DIR, 'static'), # 配置静态文件目录
)
SESSION_ENGINE='django.contrib.sesssions.backends.cache' # session默认放在数据库中
MIDDLEWARE = [
....
'middle.m1.CMiddleWare1',
]
models.py
from django.db import models
class UserType(models.Model):
# 会隐含的创建id自增主键列.
name = models.CharField(max_length=32) # 表UserType的第一列.
class UserInfo:
''' 其他参数
blank :admin中是否可以为空
help_text:admin中的提示信息
db_index :是否对该列建立索引,经常做查询key的列需要建立.
unique :唯一索引
auto_now :在更新记录的时候,该列会自动更新时间,精确到微妙!
auto_now_add :在创建新记录的时候,该列会自动插入当前时间!
'''
uid = models.AutoFiedl(primary_key=True) # 自指定自增列
username = models.CharField(verbose_name='用户名',max_length=16) # 用户名可以在views中使用ModeForm使自动取值
pwd = models.CharField(max_length=16,null=True) # 注意:若之前其他列都有好多数据了,则新添加的列必须指定值,不然会有提示!
email = models.EmailField(max_length=16) # 实际上仅能够对admin的表单数据做验证!
user_type_id = models.IntegerField(choices=((0,'超级用户'),(1,'普通用户')), default=1) # admin中会显示下拉框
# 可直接访问关联的表: row.user_type.name
# 类型是UserType.uid(默认就是主键关联),列明会被自动修改为user_type_id.
user_type = models.ForeignKey('UserType', to_field='uid')
# 通常在Views.py导入,然后取得数据库的数据后传递给render参数!
# filter()+values()过滤中使用双下划线用于跨表取数据,相当于对象的点语法!
models.UserInfo.objects.create(username='root',pwd='123') # 或-->
models.UserInfo.objects.filter(username='root').delete() # 也可以all().delete()
models.UserInfo.objects.filter(username='root').update(pwd=1) # 改密码
models.UserInfo.objects.filter(username='root').first() # 找到第1个匹配,不存在==None!
.all().values('col1','user_type__name') # [{'col1':v, 'col2':v}...],限定仅取指定的列
for r in models.UserInfo.objects.all(): # [userinfo_obj, ...]
r.id, r.username, r.pwd
admin.py
后台管理用的,必须把^admin映射添加到urls.py中!
会计自动听课脚本
目的
- 本脚本主要是辅助会计继续教育官网视频学习, 能够自动答题. 避免为凑足观看学时而一直值守观看的烦恼.
- 脚本需要放在浏览器插件Tampermonkey中执行.
// ==UserScript==
// @name answering
// @namespace http://jxjyxuexi.chinaacc.com
// @version 0.1
// @description try to take over the world!
// @author lei
// @match http://jxjyxuexi.chinaacc.com/CourseWare/*
// @grant none
// ==/UserScript==
function answering() {
// get correct answer!
var ret = commit_user_select("");
if (!ret || !ret.firstChild)
return;
var ans = ret.firstChild.innerHTML;
var ajh = ans.indexOf('。');
if (6<ans.length && ajh>5) {
ans = ans.slice(5, ajh);
if (ans === '错')
ans = 'N';
else if (ans === '对')
ans = 'Y';
// post our answer
commit_user_select(ans);
}
}
function commit_user_select(useranswervalue) {
// parent node must exist!
if (!document.getElementById('videoPointContent'))
return null;
// var useranswer = document.getElementsByName("useranswer");
// var useranswervalue = "";
// for (var i = 0; i < useranswer.length; i++) {
// if (useranswer[i].checked) {
// useranswervalue += useranswer[i].value;
// }
// }
//var pointTestUrl = "/CourseWare/1642430/9530/3434/video/PointTest";
//var nowVideoID = "0101";
var strUrl = pointTestUrl + "?rnd=" + Math.random();
try {
var xmlHttp = new XMLHttpRequest();
var sdata = "testid=" + document.pointform.testid.value + "&useranswer=" + useranswervalue +
"&pointnum=" + document.pointform.pointnum.value + "&forumid=" + document.pointform.forumid.value +
"&questionid=" + document.pointform.questionid.value + "&pointtype=" + document.pointform.pointtype.value +
"&newpointid=" + document.pointform.newpointid.value + "&videoid=" + nowVideoID;
xmlHttp.open("Post", strUrl, false);
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.send(sdata);
//xmlHttp.addEventListener();
if (xmlHttp.status == 200) {
var xmlDoc = xmlHttp.responseText;
eval("var obj =" + xmlDoc);
if (obj.Results == "1") {
document.getElementById("videoPointBg").style.display = "none";
document.getElementById("videoPoint").style.display = "none";
setValidTime(obj.MaxPlayTime);
playVideo();
} else {
document.getElementById("PointQuestionAnswer").innerHTML = obj.ReturnStr;
return document.getElementById("PointQuestionAnswer");
}
}
return null;
} catch (e) {
return null;
}
}
(function() {
setInterval(answering, 3000);
})();
Snippets
singleflight
// 执行并返回给定函数的结果,确保对于给定的键+fn只执行1次
// 如果有重复的进来,重复的调用者会等待最原始的调用完成并收到相同的结果
// 返回值 shared 指示是否将 v 提供给多个调用者
// 返回值 v 是 fn 的执行结果
// 返回值 err 是 fn 返回的 err
func (g *Group) Do(key string, fn func()(any, error)) (v any, err error, shared bool)
// 和 Do 类似,但返回一个 channel, 用来接收结果. Result 是一个结构体,有3个字段(Do返回的那3个)
func (g *Group) DoChan(key string, fn func() (any, error)) <-chan Result
// 不走Do的流程,直接调用fn回调
func (g *Group) Forget(key string)








