博客开发之旅:

# 回滚,数据存储失败时,还原修改操作
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. C# 后台处理http请求

    using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using Syst ...

  2. 题解报告:hdu 1398 Square Coins(母函数或dp)

    Problem Description People in Silverland use square coins. Not only they have square shapes but also ...

  3. 【先定一个小目标】Asp.net Core 在IIS上的托管运行

    1.安装 .NET Core Framework 下载.net core地址:官网地址 2.Install IIS 在控制面板->程序与功能->Internet Infomation Se ...

  4. tabBar隐藏方式

    如果是从A push到B,并且把A的一个东西传到B,那么在push时就要隐藏tabBar,并且要在B ViewController设置一个接收A传到的属性. 这种方式一般用在表格点选,要把表格点选的内 ...

  5. 转 PHP抽象类:无法实例化 (不错)

    http://blog.csdn.net/kaituozheboke/article/details/52183726 一.抽象类: 无法实例化 类前加 abstract, 此类就成为抽象类,无法实例 ...

  6. GUI初步和frame&panel

    java的话这个GUI其实不是什么重点,但我们也要学习,重点是学习这种图形编程的思路. java里面对于图形的一些类都封装在了AWT和它的一些子包里.AWT(抽象窗口开发包)            当 ...

  7. TCP协议三次握手和四次握手

    前言 先说一下IP协议和TCP协议,IP协议是无连接的通信协议,IP不会占用两个设备之间通信的线路,IP实际上主要负责将每个数据包路由至目的地,但是IP协议并没有能够确保数据包是否到达,传过去的数据包 ...

  8. asterisk-java ami3 属性改变监听

    asteriskServer.addAsteriskServerListener(new AsteriskListenerInit());//服务属性监听会自动连接服务 实现AsteriskServe ...

  9. Ubuntu 下更新或下载输入法(搜狗)

    ubuntu12.04的fcitx版本不支持,不满足依赖,需要更新fcitx 添加fcitx源添加fcitx源命令 : sudo add-apt-repository ppa:fcitx-team/n ...

  10. 2017huas_ACM第三天

    暑假集训第三天. 就在刚才AC了第十题,本周做题拿到了满分. 软件工程专业没有学习C++语言,这在做题过程中给了我不少的麻烦.遇到什么不懂的,不确认的,都要上网查阅或者开了新项目自己尝试一下.耗费了不 ...