博客开发之旅:

# 回滚,数据存储失败时,还原修改操作
from django.db import transaction
with transaction.atomic():
do...
... # ==========自定义form表单验证----------====
# 自定义验证规则
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
# 使用自定义验证规则
phone = fields.CharField(validators=[mobile_validate, ],
error_messages={'required': '手机不能为空'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'})) # 多选
course_id = fields.MultipleChoiceField(
choices=models.Course.objects.all().values_list('id','title'),
widget=widgets.SelectMultiple(attrs={'class':'form-control'})
)

#+============+++++++++++==扩展Django自带的用户认证表=========+++++++++++++===

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
"""
用户信息表
"""
nid = models.AutoField(primary_key=True)
phone = models.CharField(max_length=11, null=True, unique=True)
avatar = models.FileField(upload_to="avatars/", default="avatars/default.png", verbose_name="头像")
create_time = models.DateTimeField(auto_now_add=True) blog = models.OneToOneField(to="Blog", to_field="nid", null=True,on_delete=models.CASCADE) def __str__(self):
return self.username # 在settings中告诉Django项目用哪张表做认证
AUTH_USER_MODEL = 'app01.UserInfo' from django.contrib import auth user = authenticate(username='theuser',password='thepassword')
# 即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数。
# 如果认证成功(用户名和密码正确有效),便会返回一个 User 对象。 user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# 该函数接受一个HttpRequest对象,以及一个经过认证的User对象。
# 该函数实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据。 from django.contrib.auth import logout
def logout_view(request):
logout(request)
# 该函数接受一个HttpRequest对象,无返回值。
# 当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。 if not request.user.is_authenticated():
pass
# is_authenticated()
# 用来判断当前请求是否通过了认证。 # auth 给我们提供的一个装饰器工具,用来快捷的给某个视图添加登录校验。
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
pass
# 若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' 并传递当前访问url的绝对路径
(登陆成功后,会重定向到该路径)。LOGIN_URL = '/login/' # 这里配置成你项目登录页面的路由 # create_user()
# auth 提供的一个创建新用户的方法,需要提供必要参数(username、password)等。
from django.contrib.auth.models import User
user = User.objects.create_user(username='用户名',password='密码',email='邮箱',...) # create_superuser()
# auth 提供的一个创建新的超级用户的方法,需要提供必要参数(username、password)等。
from django.contrib.auth.models import User
user = User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...) # check_password(password)
# auth 提供的一个检查密码是否正确的方法,需要提供当前请求用户的密码。密码正确返回True,否则返回False。
ok = user.check_password('密码') # set_password(password)
# auth 提供的一个修改密码的方法,接收 要设置的新密码 作为参数。
注意:设置完一定要调用用户对象的save方法!!!
user.set_password(password='')
user.save() 极验: https://docs.geetest.com/install/deploy/server/python
pip install geetest

# +++++++++++++++++++++++文件上传++++++++++++++++++++

views
from django.views.decorators.csrf import csrf_exempt @csrf_exempt
def upload(request):
if request.method == 'POST':
file_obj = request.FILES.get('file')
with open('upload/'+file_obj.name,'wb')as f:
for i in file_obj.chunks():
f.write(i)
return render(request,'upload.html') html
<form action="/upload/" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="提交"/>
</form>

#++++++++++++==文档加载完之后才执行 JS 的三种方式===++++++++++++

<script src="/static/jquery-3.3.1.js"></script>
<script>
window.onload = function () {
var a = document.getElementById('username');
alert(a);
};
$(document).ready(function () {
var a = document.getElementById('default_avatar');
alert(a);
});
$(function () {
var a = document.getElementById('default_avatar');
alert(a);
})
</script>

#+++++++++++++++++====头像预览====++++++++++++++++

<div class="form-group">
<label class="col-sm-4 control-label">选择头像</label>
<div class="col-sm-8">
<label for="id_avatar">
<img id="default_avatar" src="/static/img/hmbb.png" alt="默认头像"/>
</label>
<input type="file" id="id_avatar" name="avatar" style="display: none"/>
# <input accept="image/*" type="file" id="avatar" name="avatar" style="display: none"/>
</div>
</div>
<script src="/static/jquery-3.3.1.js"></script>
<script>
$('#id_avatar').change(function () {
// 创建一个读取文件的对象
var fileReader = new FileReader();
// 读取到当前选中的文件
// console.log(this.files[0]);
fileReader.readAsDataURL(this.files[0]);
fileReader.onload = function () {
$('#default_avatar').attr('src',fileReader.result);
}
})
</script>

#-------------Form表单验证在渲染成HTML标签时显示错误信息----------------

<div class="form-group">
<label for="{{ obj.username.id_for_label }}" class="col-sm-4 control-label">
{{ obj.username.label }}
</label>
<div class="col-sm-8">
{{ obj.username }}{{ obj.errors.username.0 }}
</div>
</div>
{% for row in obj %}
<div class="form-group">
<label for="{{ row.id_for_label }}" class="col-sm-4 control-label">
{{ row.label }}
</label>
<div class="col-sm-8">
{{ row }}{{ row.errors.0 }}
</div>
</div>
{% endfor %}

# +++++++++++++++++++全局钩子验证密码一致性 以及实时跟新数据+++++++++++++++++++++

class RegForm(Form):
...
re_password = fields.CharField(
min_length=6,
label="确认密码",
widget=widgets.PasswordInput(
attrs={"class": "form-control"},
render_value=True,
),
error_messages={
"min_length": "确认密码至少要6位!",
"required": "确认密码不能为空",
}
) user_type = fields.ChoiceField(
choices=models.UserType.objects.values_list('id','caption')
) # 重写全局的钩子函数,对确认密码做校验
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password') if re_password and password != re_password:
self.add_error('re_password',ValidationError('两次密码不一致!')) else:
return self.cleaned_data # 注意返回 # 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段
# ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
def __init__(self, *args, **kwargs):
super(RegForm,self).__init__(*args, **kwargs)
self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')

# ++++==+++__++===-+_+_===# 自己生成验证码图片 # ++++==+++__++===-+_+_===#

from PIL import Image, ImageDraw, ImageFont

img_obj = Image.new('RGB',(220,40),random_color())    # 图片对象
draw_obj = ImageDraw.Draw(img_obj) # 画笔对象
font_obj = ImageFont.truetype('static/fonts/kumo.ttf', 40) # 字体对象
char_list = random_char() # 验证码字符串
request.session["code_img"] = "".join(char_list) # 将字符串保存到session会话
for i in range(len(char_list)): # 将字符画到图片上
draw_obj.text((10+50*i,0),char_list[i],fill=random_color(),font=font_obj) # draw_obj.line((begin,end),fill=random_color(),width=random.randint(1,4)) # 画线条
# draw_obj.point(width, height, fill=random_color()) # 画点
draw_obj.arc((x, y, x+z, y+z), 0, 360, fill=random_color()) # 画弧线 圆 from io import BytesIO
io_obj = BytesIO() # 将生成的图片数据保存在io对象中
img_obj.save(io_obj, "png") # 从io对象里面取上一步保存的数据
data = io_obj.getvalue()
return HttpResponse(data) ++++
<img id="get_code" src="/get_code_img/" alt="验证码加载失败">
$('#get_code').click(function () {
// 点击图片刷新验证码
$(this)[0].src += "?";
});
++++

#+==================# 重写局部钩子函数,对用户名做校验和ajax实时检验===========================+

class RegForm(Form):
username = fields.CharField(
max_length=16,
label="用户名",
error_messages={
"max_length": "用户名最长16位",
"required": "用户名不能为空",
},
widget=widgets.TextInput(
attrs={"class": "form-control"},
)
) def clean_username(self):
username = self.cleaned_data.get('username')
is_exist = models.UserInfo.objects.filter(username=username)
if is_exist:
self.add_error('username',ValidationError('用户名已存在'))
else: # 重写的是局部钩子,所以返回检验的字段
return username # username输入框失去焦点,使用ajax检验用户名是否存在
@csrf_exempt
def check_username_exist(request):
ret = {'status':False,'msg':None}
is_exist = models.UserInfo.objects.filter(username=request.POST.get('username'))
if is_exist:
ret['status']=True
ret['msg']='用户名已存在'
return HttpResponse(json.dumps(ret))
return HttpResponse(json.dumps(ret)) # <form autocomplete="off"> #取消浏览器自动匹配
# $('#id_username').on('input',function () { # 内容变动就提交
$('#id_username').blur(function () {
$.ajax({
url: '/check_username_exist/',
data: {'username': $(this).val()},
method: 'post',
dataType: 'json',
success: function (data) {
if (data.status) {
$('#id_username').next().text(data.msg);
console.log(data.msg);
}
}
})
}) # Django admin 使用
# 在app/admin.py文件中注册表
from django.contrib import admin
from app01 import models
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
# 在settings配置显示中文
LANGUAGE_CODE = 'zh-hans'
class UserInfo():
...
class Meta:
verbose_name = '用户' # 给表起名
verbose_name_plural = verbose_name # 显示复数也用'用户'来显示
在admin后台中显示中文表名
#=========== # Django用户上传的都叫media文件==================
# setting.py
# media配置,用户上传的文件都默认放在这个文件夹下
MEDIA_ROOT = os.path.join(BASE_DIR,"media")
MEDIA_URL = "/media/"
# urls.py
from django.views.static import serve
from django.conf import settings
re_path('media/(?P<path>.*)$',serve,{"document_root":settings.MEDIA_ROOT}), # 做了media配置,用户上传文件都会在media/xx中。
# _+_+_+_+_+_+_+_+_+_+_+导入Django,单独测试某个功能_+_+_+_+_+_+_+_+_+_+_+_+_+
test.py
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '博客.settings')
import django
django.setup()
from app01 import models
obj = models.UserInfo.objects.all()
print(obj.query)

# -=======----博客文章-=======----

<div class="article">
<h4><a href="">{{ article.title }}</a></h4>
<div class="media">
<div class="media-left">
<a href="#">
<img id="user_avatar" class="media-object" src="/media/{{ article.user.avatar }}"alt="...">
</a>
</div>
<div class="media-body">
<p>{{ article.desc }}</p>
</div>
</div>
<div class="article_footer">
<span><a href="">{{ article.user.username }}</a></span>发布于
<span>{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
<span class="glyphicon glyphicon-comment">评论({{ article.comment_set.count }})</span>
<span class="glyphicon glyphicon-thumbs-up">点赞({{ article.articleupdown_set.count }})</span>
</div>
</div> 分组和聚合 https://www.cnblogs.com/liwenzhou/p/8660826.html
1. 分组
ORM中values或者values_list 里面写什么字段,就相当于select 什么字段
ret = models.Employee.objects.all().values("dept", "age")
相当于:
SELECT `employee`.`dept`, `employee`.`age` FROM `employee` LIMIT 21; args=() 2. ORM中 annotate 前面是什么就按照什么分组!
from django.db.models import Avg
ret = models.Employee.objects.values("province").annotate(a=Avg("salary")).values("province", "a")
相当于:
SELECT `employee`.`province`, AVG(`employee`.`salary`) AS `a` FROM `employee` GROUP BY `employee`.`province` ORDER BY NULL LIMIT 21; args=() 3. extra --> 在执行ORM查询的时候执行额外的SQL语句
# 查询person表,判断每个人的工资是否大于2000
ret = models.Person.objects.all().extra(
select={"gt": "salary > 2000"}
)
相当于:
SELECT (salary > 2000) AS `gt`, `person`.`id`, `person`.`name`, `person`.`salary`, `person`.`dept_id` FROM `person` LIMIT 21; args=() 4. 直接执行原生的SQL语句,类似pymysql的用法
from django.db import connection
cursor = connection.cursor() # 获取光标,等待执行SQL语句
cursor.execute("""SELECT * from person where id = %s""", [1])
row = cursor.fetchone()
print(row) # =====++++++创建数据库表,插入时间++++++=======
mysql>create table test(d date, dt datetime, t time);
mysql>insert into test(d,dt,t) values(now(),now(),now());
mysql>select date_format(dt,'%Y-%m') from test; # 格式化,只看年月

#=++++++++++++==========+++++母板,子板,自定义templates+++====+++++++++++===========

母板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ blog.title }}</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css">
<link rel="stylesheet" href="/static/commons.css"/>
<link rel="stylesheet" href="/static/theme/{{ blog.theme }}"/>
</head>
<body>
<div class="blog">
<div class="header">
<div>{{ blog.title }}</div>
</div>
<div class="container">
<div class="col-md-3">
{% load my_tags %}
# 在这使用自定义templates
{% get_left_menu username %}
</div>
<div class="col-md-8">
{% block page-main %} {% endblock %}
</div>
</div>
</div>
<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</body>
</html> 子板
{% extends 'base.html' %} {% block page-main %}
<div class="article">
{% for article in article_list %}
。。。。。。。
{% endfor %}
</div>
{% endblock %} 自定义templates
在app下新建目录templatetags,在新建目录下新建文件my_tags.py
from django import template
from app01 import models
from django.db.models import Count register = template.Library() # 固定写法
@register.inclusion_tag("left_menu.html")
def get_left_menu(username):
user = models.UserInfo.objects.filter(username=username).first()
blog = user.blog
# 查询文章分类及对应的文章数
category_list = models.Category.objects.filter(blog=blog)
# 查文章标签及对应的文章数
tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
# 按日期归档
# archive_list = models.Article.objects.filter(user=user).extra(
# select={"archive_ym": "date_format(create_time,'%%Y-%%m')"}
# ).values("archive_ym").annotate(c=Count("nid")).values("archive_ym", "c") return {
"category_list" :category_list,
"tag_list": tag_list,
} 创建left_menu.html
<div class="panel panel-primary">
<div class="panel-heading">文章分类</div>
<div class="panel-body">
{% for category in category_list %}
<p>{{ category.title }}({{ category.article_set.all.count }})</p>
{% endfor %}
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">标签分类</div>
<div class="panel-body">
{% for tag in tag_list %}
<p>{{ tag.title }}({{ tag.c }})</p>
{% endfor %}
</div>
</div>

# ===========-----====-----====-------点赞 js-------======-----======-----=============

def dianzan_up_down(request):
ret = {'status':False,'msg':None}
article_id = request.POST.get('article_id')
is_up = json.loads(request.POST.get('is_up'))
user = request.user
if user:
try:
models.ArticleUpDown.objects.create(user=user,article_id=article_id,is_up=is_up)
models.Article.objects.filter(nid=article_id).update(up_count=F('up_count')+1)
ret['status'] = True
ret['is_up'] = is_up
return HttpResponse(json.dumps(ret))
except Exception as e:
is_up = models.ArticleUpDown.objects.filter(user=user,article_id=article_id).first().is_up
ret['first_action']=is_up
return HttpResponse(json.dumps(ret))
ret['msg'] = '请先登录'
return HttpResponse(json.dumps(ret)) <div class="dianzan_up_down">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color: red;"></div>
</div> {% csrf_token %}
# 可以将文章id渲染出来 <div class="info" article_id="{{ article.pk }}"></div> </div>
<script> // 点赞
$('#div_digg .action').click(function () {
var is_up = $(this).hasClass('diggit');
var crticle_id = "{{ article.pk }}";
# var article_id = $('.info').attr('article_id');当JS保存为静态文件时,获取文章id
$.ajax({
url: '/blog/article/up_down/',
type: 'post',
data: {
'article_id': crticle_id,
'is_up': is_up,
'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val()
},
dataType: 'json',
success: function (data) {
if (data.status) {
if (data.is_up) {
var count = $('#digg_count').text();
count = parseInt(count) + 1;
$('#digg_count').text(count);
} else {
var count = $('#bury_count').text();
count = parseInt(count) + 1;
$('#digg_count').text(count);
}
} else {
if (data.msg) {
$('#digg_tips').text(data.msg);
} else {
if (data.first_action) {
$('#digg_tips').text('您已经推荐过啦');
} else {
$('#digg_tips').text('您已经反对过啦');
}
}
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
}) })
</script> # ========= 响应ajax,数据直接使用,不用写dataType:'json' =========
from django.http import JsonResponse
return JsonResponse({'',''})

# +++++++++++++++++++使用JS动态绑定事件+++++++++++++++++++++++

    //後面添加的元素無法綁定事件,需預加載
$(document).on('click','#reply',function () {
$("#comment_content").focus();
var v = "@" + $(this).attr("username") + "\n";
$("#comment_content").val(v);
pid = $(this).attr("comment_id")
});

#_+__________++++++++评论树例子+__________+++++++++

from django.http import JsonResponse
def comment_tree(request,article_id):
comment_list = list(models.Comment.objects.filter(article_id=article_id).values("pk","content","parent_comment_id","create_time",'user__username'))
print(comment_list) # 将<QuerySet [{'pk': 6,}]> 转为列表。若直接json.dumps(comment_list)会报错!
return JsonResponse(comment_list,safe=False) # 静态文件中(外部js文件)需要使用的值,最好在渲染的时候将值作为属性存到标签中。方便取值。
$(function () {
// 获取评论列表
$.ajax({
url: '/blog/comment/' + '{{ article.pk }}/',
// dataType:'json',
// return HttpResponse(json.dumps(dict(enumerate(comment_list))))
// enumerate(comment_list,start=1)指定起始值,那么下面的.each index就不用加值了,不过这个麻烦
// 瞎折腾得是,以上3行 等价 return JsonResponse(comment_list,safe=False)
success: function (data) {
$.each(data, function (index, comment_dict) {
index = index + 1;
var s = '<div class="comment_item well" comment_id="' + comment_dict.pk + '">\n' +
' <div class="left">\n' +
' <a href="">#' + index + '楼</a>\n' +
' <span>' + comment_dict.create_time + '</span>\n' +
' <a href="/blog/' + comment_dict.user__username + '/">' + comment_dict.user__username + '</a>\n' +
' </div>\n' +
' <div class="right">\n' +
' <a id="reply" comment_pk="' + comment_dict.pk + '" username="' + comment_dict.user__username + '">回复</a>\n' +
' </div>\n' +
' <div class="clear_float_before"><span>' + comment_dict.content + '</span></div>\n' +
' </div>'; if (comment_dict.parent_comment_id) {
// 子评论 追加到父评论下
var pid = comment_dict.parent_comment_id;
$('[comment_id="' + pid + '"]').append(s);
} else {
// 根评论
$('.comment_tree').append(s);
}
})
}
}); pid = ""; // 有值即回复别人的评论内容,无值评论文章
// 子评论设置pid
$(document).on('click', '#reply', function () {
var v = "@" + $(this).attr("username") + "\n";
$("#comment_content").focus().val(v);
pid = $(this).attr("comment_pk");
}); // 提交评论
$('#comment_btn').click(function () {
var content = $('#comment_content').val();
var article_id = $('#info').attr('article_id'); $.ajax({
url: '/blog/comment/',
type: 'post',
data: {
'content': content, 'article_id': article_id, 'pid': pid,
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
},
success: function (data) {
var s = '<div class="well"><span>' + content + '</span></div>';
// 生成tag,添加到页面暂不刷新,清除文本框,将pid清空,避免影响提交数据。
$('.comment_tree').append(s);
$('#comment_content').val('');
pid = "";
}
})
})
})

# ===========================富文本编辑器=kindeditor===============================

<textarea name="article_content" id="article_content" cols="" rows=""></textarea>
<script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script>
<script src="/static/jquery-3.3.1.js"></script>
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#article_content', {
width: '800px',
uploadJson: "/upload/", //上传图片什么的需要填参数
extraFileUploadParams: {
"csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val()
},
filePostName: "upload_file",
//request.FILE.get('') 文件键名
});
});
</script> 上传图片
def upload(request):
if request.method == 'POST':
file_obj = request.FILES.get('upload_file')
path = 'media/add_article/'+file_obj.name
with open(path,'wb')as f:
for i in file_obj.chunks():
f.write(i) res = {
'error':0, # 没有出错
'url':path,
}
return HttpResponse(json.dumps(res))
return HttpResponse('') 提交文章 # 使用beautifulSoup过滤文章中的JS代码,防止XSS攻击 def add_article(request):
if request.method=="POST":
title=request.POST.get('title')
article_content=request.POST.get('article_content')
user=request.user from bs4 import BeautifulSoup
bs = BeautifulSoup(article_content,'html.parser')
# 过滤非法字符
for tag in bs.find_all():
# print(tag.name)
if tag.name in ['script','link']:
tag.decompose() desc = bs.text[0:150]+'...'
article_obj = models.Article.objects.create(user=user,title=title,desc=desc)
models.ArticleDetail.objects.create(content=str(bs),article=article_obj) return redirect('/blog/%s/'%request.user.username)
return render(request,'add_article.html') # orm查询,基于对象查询(子查询),反向查询按表名小写_set.all()
# 基于queryset和__查询(join查询)正向查询:按字段 反向查询:表名小写
# select publish.email from Book
# left join Publish on book.publish_id=publish.nid
# where book.title="python"
# 按逻辑来,对象查询是基于单个对象!?,join只要连上表就能拿值!。

# ———————————————————————————————————简单使用admin—————————————————————————————————————

from django.utils.safestring import mark_safe
from app01 import models class UserInfoConfig(admin.ModelAdmin): def deletes(self):
return mark_safe("<a href=''>删除</a>") list_display = ["username","email","create_time",'blog',deletes]
# 在admin管理页面,显示出用户表的用户信息字段,deletes是自定义的跳转链接 list_display_links = ["email"]
# 设置哪个字段可以点击跳转到编辑当前页信息的页面 list_filter=["username","email","create_time",'blog']
# 筛选功能,按字段条件筛选,指定多个字段组合筛选。 list_editable=["username",'blog']
# 此配置需要有list_display_links = ["email"]配置才能生效,即在当前页编辑其他字段信息,效果和直接在被编辑字段的编辑页面相同 search_fields=["username","email"]
# 添加搜索功能,以列表中的字段过滤出的信息后进行查找 def patch_init(self,request,queryset):
queryset.update(price=100)
patch_init.short_description = "批量初始化"
actions = [patch_init,]
# 添加一个批量操作选项。传入执行方法名 change_list_template="login.html"
# 自己的后台管理页面。

# -------------------Xadmin----------------------

流程
1、启动
在settings文件中配置上 'Xadmin.apps.XadminConfig',
将会自动执行ready方法,查找所有app中的Xadmin模块
autodiscover_modules('Xadmin') 2、注册
单例模式
from Xadmin.service.Xadmin import site
from app01 import models
site.registry(models.Test) 3、设计url
为每个app下的model设计增删改查url
127.0.0.1:8008/admin/app01/book/1/change/ :改id=1的数据 # url的路由分发
path('test/',([re_path('\d+',func),re_path('(\d+)',([],None,None)),],None,None)) # url
path('xadmin/', Xadmin.site.urls), # Xadmin组件 -> apps.py
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules class XadminConfig(AppConfig):
name = 'Xadmin'
def ready(self):
autodiscover_modules('Xadmin') # Xadmin -> service -> Xadmin.py
from django.urls import path,re_path
from django.shortcuts import render,HttpResponse,redirect class ModelXadmin(object):
def __init__(self,model,site):
self.model=model
self.site=site def list_view(self,request):
return HttpResponse('list_view')
def add_view(self,request):
return HttpResponse('add_view')
def change_view(self,request,id):
return HttpResponse('change_view')
def delete_view(self,request,id):
return HttpResponse('delete_view') def get_urls2(self):
temp=[]
temp.append(re_path('^$',self.list_view))
temp.append(re_path('^add/$',self.add_view))
temp.append(re_path('^(\d+)/change/$',self.change_view))
temp.append(re_path('^(\d+)/delete/$',self.delete_view))
return temp @property
def urls2(self):
# 路由,某个表的增删改查url
return self.get_urls2(),None,None class XadminSite(object):
def __init__(self,name='admin'):
self._registry = {} def get_urls(self):
temp = []
for model,xadmin_class_obj in self._registry.items():
app_name = model._meta.app_label
model_name = model._meta.model_name
temp.append(re_path('^%s/%s/'%(app_name,model_name),xadmin_class_obj.urls2))
return temp @property
def urls(self):
# 路由,app下的某个表url
return self.get_urls(),None,None def registry(self,model,xadmin_class=None,**kwargs):
if not xadmin_class:
xadmin_class = ModelXadmin self._registry[model] = xadmin_class(model,self)
site = XadminSite() # app01 -> Xadmin.py
from Xadmin.service.Xadmin import site
from app01 import models
site.registry(models.Test) 笔记暂断

# =-=-=-=-=-=-=-=-=rbac基于角色的访问权限控制,大致流程=--==-=-=-=-=-=-=-

# rbac -> views.py
from django.shortcuts import render,redirect
from rbac.models import * def rbac_list(request):
'''用户权限信息'''
role_list = Role.objects.all()
user = Role.objects.values('o2o_user__user_id','o2o_user__user__username')
user_list = []
for item in user:
if item['o2o_user__user_id']:
user_list.append((item['o2o_user__user_id'],item['o2o_user__user__username'])) obj = roleForm()
return render(request,'rbac.html',{'role_list':role_list,'user_list':user_list,'obj':obj}) from django.forms import Form,fields,widgets
class roleForm(Form):
users = fields.ChoiceField(
choices=O2o_User.objects.values_list('user__nid','user__username')
)
roles = fields.MultipleChoiceField(
choices=Role.objects.values_list('pk','title')
) def rbac_edit(request):
'''修改权限'''
id = request.POST.get('users')
roles = request.POST.getlist('roles')
obj = roleForm({'users':id,'roles':roles})
res = obj.is_valid()
if res:
obj = O2o_User.objects.filter(user_id=id).first()
obj.roles.set(roles)
return redirect('/rbac/') # 中间件过滤请求
import re
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect class ValidPermission(MiddlewareMixin):
def process_request(self,request):
current_path = request.path_info pass_url = ["/index/",'/info/','/register/.*','/login/.*','/logout/','/admin/.*',]
# 白名单直接放行
for rule in pass_url:
ret = re.match(rule, current_path)
if ret:
return None # 不在白名单内,判断是否登录用户。
if not request.user.username:
return redirect('/login/') permission_dict = request.session.get('permission_dict')
for item in permission_dict.values():
urls = item['url']
for rule in urls:
rule = '^%s$'%rule
ret = re.match(rule,current_path)
if ret:
# 注入权限,用于控制template模板显示增删改
request.action = item['action']
return None
print('没有权限%s'%current_path)
return HttpResponse('没有权限%s'%current_path) # rbac -> permission.py
from rbac import models def valid_permission(request):
'''提取用户权限并存储到session'''
# print('ssssssssssssssss',request.path_info,user.email)
res = models.O2o_User.objects.filter(user=request.user).values(
'roles__permissions__url',
# 'roles__permissions__title',
'roles__permissions__action',
'roles__permissions__group_id',)
per = {}
for item in res:
if item['roles__permissions__group_id'] in per:
per[item['roles__permissions__group_id']]['url'].append(item['roles__permissions__url'])
# per[item['roles__permissions__group_id']]['title'].append(item['roles__permissions__title'])
per[item['roles__permissions__group_id']]['action'].append(item['roles__permissions__action'])
else:
per[item['roles__permissions__group_id']] = {
'url':[item['roles__permissions__url'],],
# 'title':[item['roles__permissions__title'],],
'action':[item['roles__permissions__action'],]
}
request.session['permission_dict'] = per
print(per) # 使用auth认证,登录成功就将用户权限保存到session中
auth.login(request,user)
valid_permission(request) # 在rbac中创建一个模板用于rbac权限设置,用户权限不满足就只渲染部分功能。

# -===-=-=-=-=-=-=-=-=-=-=-=-=-返回顶部=-=-=-=-=-=-=-=-

<span id="back_top" class="hidden">返回顶部</span>
<script>
$(window).scroll(function () {
var $height = $(window).scrollTop();
if ($height > 200) {
$('#back_top').removeClass('hidden');
} else {
$('#back_top').addClass('hidden');
}
})
$('#back_top').click(function () {
$('body,html').animate({
scrollTop: 0
}, 1000);
})
</script>

# -=-=-=-=-=-=-=-ajax请求后端返回对象,前端直接使用

comment_list = list(models.Comment.objects.filter(article_id=article_id).values("pk","content"))
return JsonResponse(comment_list, safe=False) 传送对象 success: function (data) {
$.each(data, function (index, comment_dict) {
comment_dict.pk
}}

#-=-=-=-==========页面跳转=-=-=-=-=-=-=-=-=-

<script language="javascript" type="text/javascript">
// 以下方式直接跳转
window.location.href='hello.html';
// 以下方式定时跳转
setTimeout("javascript:location.href='hello.html'", 5000);
</script> <head>
<!-- 以下方式只是刷新不跳转到其他页面 -->
<meta http-equiv="refresh" content="">
<!-- 以下方式定时转到其他页面 -->
<meta http-equiv="refresh" content="5;url=hello.html">
</head> # 结合了倒数的javascript实现
<script language="javascript" type="text/javascript">
var second = document.getElementById('totalSecond').textContent;
setInterval("redirect()", 1000);
function redirect()
{
document.getElementById('totalSecond').textContent = --second;
if (second < 0) location.href = 'hello.html';
}
</script>

python 博客开发之散乱笔记的更多相关文章

  1. Django博客开发实践,初学者开发经验

    python,Django初学者,开发简易博客,做了一下笔记,记录了开发的过程,功力浅薄,仅供初学者互相 交流,欢迎意见建议.具体链接:Django博客开发实践(一)--分析需求并创建项目 地址:ht ...

  2. Django 博客开发教程目录索引

    Django 博客开发教程目录索引 本项目适合 0 基础的 Django 开发新人. 项目演示地址:Black & White,代码 GitHub 仓库地址:zmrenwu/django-bl ...

  3. Django个人博客开发 | 前言

    本渣渣不专注技术,只专注使用技术,不是一个资深的coder,是一个不折不扣的copier 1.前言 自学 Python,始于 Django 框架,Scrapy 框架,elasticsearch搜索引擎 ...

  4. django 简易博客开发 5 markdown支持、代码高亮、gravatar头像服务

    上一篇博客介绍了comments库使用及ajax支持,现在blog已经具备了基本的功能,但是只能发表文字,不支持富文本编辑.今天我们利用markdown添加富文本支持. markdown语法说明: h ...

  5. django 简易博客开发 4 comments库使用及ajax支持

    首先还是贴一下源代码地址  https://github.com/goodspeedcheng/sblog 上一篇文章我们介绍了静态文件使用以及如何使用from实现对blog的增删改,这篇将介绍如何给 ...

  6. django 简易博客开发 2 模板和数据查询

    首先还是贴一下项目地址  https://github.com/goodspeedcheng/sblog   因为代码全在上面 上一篇博客我们介绍了 django的安装配置,新建project,新建a ...

  7. django 简易博客开发 1 安装、创建、配置、admin使用

    首先贴一下项目地址吧  https://github.com/goodspeedcheng/sblog 到现在位置项目实现的功能有: 1.后台管理使用Admin ,前端显示使用bootstrap 2. ...

  8. Django博客开发-数据建模与样式设定

    开发流程介绍 之前Django的学习过程当中已经把基本Django开发学完了,现在以Django 的博客项目完成一遍课程的回顾和总结.同时来一次完整开发的Django体验. 一个产品从研究到编码我们要 ...

  9. 微信小程序版博客——开发汇总总结(附源码)

    花了点时间陆陆续续,拼拼凑凑将我的小程序版博客搭建完了,这里做个简单的分享和总结. 整体效果 对于博客来说功能页面不是很多,且有些限制于后端服务(基于ghost博客提供的服务),相关样式可以参考截图或 ...

随机推荐

  1. [App Store Connect帮助]七、在 App Store 上发行(2.4)设定价格与销售范围:安排价格调整

    如果您拥有<付费应用程序协议>,则可以为您的 App 安排随时间推移的价格调整.您可以安排具有明确开始日期和结束日期的定价调整,以及没有结束日期的永久性定价调整.例如,您可以设置一个为期 ...

  2. Mybatis的全局配置文件标签介绍(mybatis-config.xml)

    全局配置文件中本人只记录了常用的几个  typeHandlers, objectFactory,objectWrapperFactory, reflectorFactory, plugins, dat ...

  3. 定时清除 /var/log/massage 下的信息脚本文件

    定时清除 /var/log/massage 下的信息脚本 #!/bin/sh #Date: 0:07 #Author: Xiaodong #Mail: 990974238@qq.com #Puncti ...

  4. MongoDB操作简记

    一.数据库操作 1.显示当前选择的数据库 [root@weekend05 ~]# mongod --dbpath /data/db/ [root@weekend05 ~]# mongo MongoDB ...

  5. [转][ASP.NET MVC]如何定制Numeric属性/字段验证消息

    本文转自:http://www.cnblogs.com/artech/archive/2012/02/13/NumericPropertyValidation.html 对于一个Numeric属性/字 ...

  6. python 使用 Pyscript 调试 报错

    UnicodeEncodeError: 'ascii' codec can't encode characters in position 13-16: ordinal not in range(12 ...

  7. ES之事件绑定,解除绑定以及事件冒泡、事件捕获

    绑定事件的处理方法任何元素都有事件属性,而绑定事件就是将这个事件与一个函数相连接. ①句柄事件dom.onXXX = function () {代码块} 以on开头的事件属于句柄事件兼容性非常好,但是 ...

  8. Android开发中查看未root真机的app数据库

    在Android开发中,如果用到数据库来储存数据,那么难免就要查看数据库中的内容,可是对于未root的真机来说,查看数据库就不是那么容易了,如果仅仅为了查看数据库再把手机root了,有点得不偿失,所以 ...

  9. Python3 动手自己写谷歌翻译

    本篇为实现谷歌翻译的功能,在编写的时候以为只是一个接口的问题. 没想到的是每次翻译都会触发一次JS的执行,在请求参数中生成一个tk. 文中tk的实现是复用的网上大神的代码生成tk. 好了,不说了直接看 ...

  10. iOS Programming Views :Redrawing and UIScrollView

    iOS Programming Views :Redrawing and UIScrollView  1.1 event  You are going to see how views are red ...