python 博客开发之散乱笔记

博客开发之旅:

# 回滚,数据存储失败时,还原修改操作
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'})
    )
        
View Code

 

#+============+++++++++++==扩展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
View Code

 

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

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>
View Code

 

#++++++++++++==文档加载完之后才执行 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>
View Code

 

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

<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>
View Code

 

#-------------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 %}
View Code

 

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

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')
View Code

 

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

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 += "?";
});
++++
View Code

 

#+==================# 重写局部钩子函数,对用户名做校验和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后台中显示中文表名
View Code

 

#=========== # 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中。
View Code
# _+_+_+_+_+_+_+_+_+_+_+导入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)
View Code

 

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

<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;    # 格式化,只看年月
        
        
        
View Code

 

#=++++++++++++==========+++++母板,子板,自定义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>
View Code

 

# ===========-----====-----====-------点赞 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({'',''})
View Code

 

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

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

 

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

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 = "";
            }
        })
    })
})
View Code

 

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

<textarea name="article_content" id="article_content" cols="60" rows="20"></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只要连上表就能拿值!。
View Code

 

# ———————————————————————————————————简单使用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"
    # 自己的后台管理页面。
View Code

# -------------------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)

笔记暂断
View Code

 

# =-=-=-=-=-=-=-=-=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权限设置,用户权限不满足就只渲染部分功能。
View Code

 

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

<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>
View Code

 

# -=-=-=-=-=-=-=-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
}}
View Code

 

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

<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="10">
<!-- 以下方式定时转到其他页面 -->
<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>
View Code

 

posted @ 2018-10-30 22:00  ming-yuan  阅读(245)  评论(0编辑  收藏  举报