點擊頭像==點擊inputcss
頭像預覽:html
修改 獲取用戶選中的文件對象;獲取文件對象的路徑;修改img標籤的src屬性,讓src=文件對象路徑。jquery
register.htmlweb
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css"> <style> #avatar{ {# 隱藏file的input標籤 #} display: none } #avatar_img{ {# 默認頭像右移20px #} margin-left: 20px; } .error_info{ color: red; } </style> </head> <body> <h3>註冊頁面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3" > <form id="form"> {% csrf_token %} {% for filed in form %} // for循環這個form就是有user,pwd,re_pwd,email這些字段,進行渲染 <div class="form-group"> {# <label for="">{{ filed.label }}</label>#} <label for="{{ filed.auto_id }}">{{ filed.label }}</label> {# 因爲forms組件渲染的input標籤中的id都會自動加上_id,因此在label標籤這裏的for爲了能保持一致,#} {#寫上filed.auto_id ,會自動的補上_id#} {{ filed }} // 它渲染出來有input標籤,會跟字段的名字一致,只不過這裏要發ajax </div> {% endfor %} <div class="form-group"> {# 頭像沒有放到input裏邊,把它單獨處理,它不須要檢驗#} <label for="avatar"> {# input中的id和label中的for一致,則點擊頭像和圖片均可以跳轉到input上面 #} 頭像 <img width="60" height="60" id="avatar_img" src="/static/blog/imgs/default.png" alt=""> </label> <input type="file" id="avatar" > {# 此時這個標籤沒有意義了,須要隱藏起來,style="display:None" #} </div> <input type="button" class="btn btn-default reg_btn " value="submit"><span class="error"></span> </form> </div> </div> </div> </body> </html>
Myforms.pyajax
# -*- coding:utf-8 -*- from django import forms from django.forms import widgets from blog.models import UserInfo from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class UserForm(forms.Form): user = forms.CharField(max_length=32, error_messages={"required": "用戶名不能爲空!"}, label="用戶名", # label標籤,這樣子就能夠渲染出中文了 widget=widgets.TextInput(attrs={"class": "form-control"}) ) # 爲這個字段添加類form-control,讓頁面更好看 pwd = forms.CharField(max_length=32, label="密碼", widget=widgets.PasswordInput(attrs={"class": "form-control"}) ) re_pwd = forms.CharField(max_length=32, label="確認密碼", widget=widgets.PasswordInput(attrs={"class": "form-control"}) ) email = forms.EmailField(max_length=32, label="郵箱", widget=widgets.EmailInput(attrs={"class": "form-control"}) )
把img標籤寫到label裏邊去,點擊頭像就至關於點擊input標籤數據庫
http://127.0.0.1:8000/static/blog/img/default.png,這樣子就能夠訪問到這個默認的註冊頭像django
不少網站是這樣子:把input標籤的長寬跟這個頭像一致,而後利用css裏邊定位position=absolute,把這兩個標籤訂位到一個位置上去。再把input標籤透明度顯示爲0,這時候它倆疊到一塊兒了,你點擊頭像也就點擊了input標籤。 另一種思路是:利用label標籤的for的值與input標籤的id值一致就能夠了,點擊label至關於點擊input標籤。bootstrap
Console---》瀏覽器
$("#avatar") r.fn.init [input#avatar]0: input#avatarlength: 1__proto__: Object(0) $("#avatar")[0] <input type="file" id="avatar"> $("#avatar")[0].files FileList {0: File(593239), length: 1}0: File(593239) {name: "distance.png", lastModified: 1510650980163, lastModifiedDate: Tue Nov 14 2017 17:16:20 GMT+0800 (中國標準時間), webkitRelativePath: "", size: 593239, …}length: 1__proto__: FileList $("#avatar")[0].files[0] File(593239) {name: "distance.png", lastModified: 1510650980163, lastModifiedDate: Tue Nov 14 2017 17:16:20 GMT+0800 (中國標準時間), webkitRelativePath: "", size: 593239, …}
頭像預覽:app
修改用戶選中的文件對象;獲取文件對象的路徑;修改img的src屬性,src=文件對象路徑。
<script src="/static/js/jquery-3.3.1.js"></script> <script> $("#avatar").change(function () { // change事件,選中文本以後事件就發生了, // 頭像預覽: // 1.獲取用戶選中的文件對象 var file_obj = $(this)[0].files[0]; // 2.獲取文件對象的路徑 // 基於文件閱讀器 FileReader,new一個實例對象 var reader = new FileReader(); reader.readAsDataURL(file_obj);// 讀完以後沒有返回值,而是默認加到你的對象裏面 // FileReaderURL是讀取文件的URL // 3.修改img的src屬性,src=文件對象的路徑 // attr給屬性賦值 // reader.readAsDataURL(file_obj)讀的時間很長,並且他們都是異步的,尚未讀完,就已經執行下面的替換操做了,會找不到路徑 // $("#avatar_img").attr("src", reader.result) // 等讀完以後在執行下面這句代碼 reader.onload = function () { $("#avatar_img").attr("src", reader.result) // 結果在reader.result裏邊 } }); </script>
點擊提交(什麼都不輸入直接submit)的錯誤信息在這裏邊:---->>> { user:null,msg:{ } }
<script> // 基於ajax提交數據 $(".reg_btn").click(function () { // ajax上傳文件類型必定要換成formdata格式 // 使用formdata,必須加上這兩個, // contentType:false, // processData:false, // 不然會報錯 var formdata = new FormData(); {# formdata.append("user", $("#id_user").val());#} {# formdata.append("pwd", $("#id_pwd").val());#} {# formdata.append("re_pwd", $("#id_re_pwd").val());#} {# formdata.append("email", $("#id_email").val());#} {# formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());#} formdata.append("avatar", $("#avatar")[0].files[0]); // 循環往formdata添加數據 {# console.log($("#form").serializeArray()); // 打印form表單全部的鍵值對#} var request_data = $("#form").serializeArray(); $.each(request_data, function (index,data) { formdata.append(data.name, data.value); }); $.ajax ({ url:"", type:"post", contentType:false, processData:false, data:formdata, // 把上邊的formdata構建出來之後,加到這裏邊來 success:function (data) { //console.log(data) if (data.user){ // 註冊成功,跳轉到登陸頁面 location.href = "/login/" } else { // 註冊失敗 // 因爲每次展現的時候會將錯誤的信息保存在span中,須要清空,不然會出現問題 $("span.error_info").text(""); $(".form-group").removeClass("has-error"); //展現以前,移除類has-error // 循環展現這次提交的錯誤信息 $.each(data.msg, function (field, error_list){ // 判斷是否是個全局的 if (field=="__all__"){ $("#id_re_pwd").next().text(error_list[0]).parent().addClass("has-error"); // 鏈式操做 } // 顯示錯誤信息:next()是找到下一個標籤 $("#id_"+field).next().text(error_list[0]); // 顯示紅色邊框:找到父標籤,而後添加一個bootstrap的類has-error $("#id_"+field).parent().addClass("has-error"); }) } } }) }) </script>
views.py
def register(request): # if request.method == "POST": # 在這裏兩者均可以 if request.is_ajax(): # 點擊那個按鈕,既是Ajax請求,又是post請求 print(request.POST) form = UserForm(request.POST) # 經過UserForm獲得用戶輸入的數據進行校驗 response = {"user": None, "msg": None} if form.is_valid(): response["user"] = form.cleaned_data.get("user") # 生成一條用戶記錄 else: print(form.cleaned_data) # 乾淨數據都在這裏邊 print(form.errors) # 錯誤數據都在這裏邊 response["msg"] = form.errors # 失敗以後把全部的錯誤信息放到msg裏邊 return JsonResponse(response) form = UserForm() print(form) return render(request, "register.html", {"form": form})
Myforms.py
# 加局部鉤子,驗證用戶是否註冊 def clean_user(self): val = self.cleaned_data.get("user") # 取到已經在上面經過基礎的格式驗證的user user = UserInfo.objects.filter(username=val).first() # 在數據庫中過濾是否有一致的用戶名 if not user: return val else: raise ValidationError("該用戶已註冊!") # 加全局鉤子,驗證兩次密碼是否一致,全局錯誤信息是放在__all__中 def clean(self): pwd = self.cleaned_data.get("pwd") re_pwd = self.cleaned_data.get("re_pwd") if pwd and re_pwd: # 當兩次密碼輸入都有值的時候纔會對二者進行比較 if pwd == re_pwd: return self.cleaned_data else: raise ValidationError("兩次密碼輸入不一致!") else: return self.cleaned_data
全局錯誤能夠在視圖裏邊取出來,跟以前form表單提交錯誤信息不同,以前是把錯誤信息放到視圖裏邊,放到模板裏邊去。這裏能夠直接在register裏邊
錯誤信息在errors裏邊,裏邊有個鍵叫__all__,對應列表error_list的信息,發給ajax了,ajax拿到數據作下判斷---->>>
if (field == "__all__"){ //處理全局錯誤信息;下面找到它的標籤,父標籤給它加上 $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error"); //給它放到re_pwd確認密碼的下面 }
class UserInfo(AbstractUser): # 使用用戶認證組件須要使用auth_user表,擴展這個表須要繼承AbstractUser,User繼承的就是AbstractUser """ 用戶信息 用戶信息表和博客信息表是一對一的關係 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") # 存本地用戶圖像字段 avatar_obj = request.FILES.get("avatar") UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj) # 這裏avatar必定要接收一個文件對象 Django實現: 會將文件對象下載到項目的根目錄中avatars文件夾中(若是沒有avatars文件夾,Django會自動建立), user_obj的avatar存的是文件的相對路徑
views.py
def register(request): # if request.method == "POST": # 在這裏兩者均可以 if request.is_ajax(): print(request.POST) form = UserForm(request.POST) # 經過UserForm都到用戶輸入的數據 response = {"user": None, "msg": None} if form.is_valid(): response["user"] = form.cleaned_data.get("user") # 生成一條用戶記錄 user = form.cleaned_data.get("user") pwd = form.cleaned_data.get("pwd") email= form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar") UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj) else: print(form.cleaned_data) print(form.errors) response["msg"] = form.errors return JsonResponse(response) form = UserForm() print(form) return render(request, "register.html", {"form": form})
Django有兩種靜態文件:
/static/ :js,css,img
/media/ :用戶上傳文件; 用戶默認頭像圖片應該在media/avatar裏邊存一份。
class UserInfo(AbstractUser):
'''用戶信息'''
nid = models.AutoField(primary_key=True)
telephone = models.CharField(max_length=11, null=True, unique=True)
avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")
avatar_obj = request.FILES.get("avatar")
user_obj = UserInfo.objects.create_user(username = user, password = pwd, email = email, avatar = avatar_obj)
用戶一旦配置了
MEDIA_ROOT = os.path.join(BASE_DIR, "media") 這個路徑(在settings裏邊)
Django會實現:
會將文件對象下載到MEDIA_ROOT中avatars文件夾中(若是沒有avatar文件夾,Django會自動建立),user_obj的avatar存的是文件的相對路徑。
settings.py
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,"static") ] MEDIA_ROOT = os.path.join(BASE_DIR, "media")
訪問http://127.0.0.1:8000/static/blog/img/default.png是能夠直接訪問到的,若是是http://127.0.0.1:8000/blog/views.py/是訪問不到的,不能把你的源代碼暴露出來啊,static裏邊的無所謂,而static是Django給你配置好的,應該讓用戶看到。一樣的media裏邊應該也讓用戶看到。 瀏覽器如何能直接訪問到media中的數據呢?
settings.py
MEDIA_URL = '/media/'
#與用戶上傳相關的配置 MEDIA_ROOT = os.path.join(BASE_DIR, "media") MEDIA_URL = '/media/'
urls.py
#media配置
re_path(r"media/(?P<path>.*)$", serve, {"document_root":settings.MEDIA_ROOT})
瀏覽器訪問http://127.0.0.1:8000/media/avatars/11.jpg能夠直接訪問media裏邊的文件了
注意規範問題:
優化代碼問題:
views.py
def register(request): # if request.method == "POST": # 在這裏兩者均可以 if request.is_ajax(): print(request.POST) form = UserForm(request.POST) # 經過UserForm都到用戶輸入的數據 response = {"user": None, "msg": None} if form.is_valid(): response["user"] = form.cleaned_data.get("user") # 生成一條用戶記錄 user = form.cleaned_data.get("user") pwd = form.cleaned_data.get("pwd") email= form.cleaned_data.get("email") avatar_obj = request.FILES.get("avatar") # 若是用戶沒有上傳圖像怎麼辦? # 沒有會有個默認的圖像,可是下面這段代碼已經上傳了一個空的avatar_obj,不會走到默認 # 因此須要加一個判斷 # UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj) # 重複代碼太多,優化 # if avatar_obj: # UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj) # else: # UserInfo.objects.create_user(username=user, password=pwd, email=email) # def create_user(self, username, email=None, password=None, **extra_fields): # create_user中有個參數**extra_fields,因此能夠下面這樣來優化 extra = {} if avatar_obj: extra['avatar'] = avatar_obj UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) else: print(form.cleaned_data) print(form.errors) response["msg"] = form.errors return JsonResponse(response) form = UserForm() print(form) return render(request, "register.html", {"form": form})