博客開發之旅:javascript
# 回滾,數據存儲失敗時,還原修改操做 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自帶的用戶認證表=========+++++++++++++===css
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
# +++++++++++++++++++++++文件上傳++++++++++++++++++++html
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>
#+++++++++++++++++====頭像預覽====++++++++++++++++java
<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標籤時顯示錯誤信息----------------python
<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 %}
# +++++++++++++++++++全局鉤子驗證密碼一致性 以及實時跟新數據+++++++++++++++++++++mysql
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')
# ++++==+++__++===-+_+_===# 本身生成驗證碼圖片 # ++++==+++__++===-+_+_===#jquery
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實時檢驗===========================+git
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)
# -=======----博客文章-=======----ajax
<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="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只要連上表就能拿值!。
# ———————————————————————————————————簡單使用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="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>