環境准備
1、 創建一個Django后,Django的目錄:
│ manage.py
│
├─Django_s1
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│
│ templates
創建app,通過命令:
python manage.py startapp app01
創建app之后的目錄結構如下:
│ manage.py
│
├─app01
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ │ __init__.py
│ │
│ └─migrations
│ __init__.py
│
├─Django_s1
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ │ __init__.py
│ │
│
└─templates
2、 創建一個項目之后首先需要做的配置
Django_s1目錄下settings.py文件中需要做如下配置:
- 找到MIDDLEWARE部分,將第四行內容注釋掉:
'django.middleware.csrf.CsrfViewMiddleware',
- 找到TEMPLATES部分,如果沒有如下代碼,則需要添加:
'DIRS': [os.path.join(BASE_DIR, 'templates')]
- 文件的最后添加靜態文件目錄,需要注意的是這里是一個元組沒所以后面的逗號必須添加
STATICFILES_DIRS=( os.path.join(BASE_DIR,"static"), )
同時需要創建一個名字為static的文件夾,用於存放靜態文件
3、 到此為止基本的配置就已經完成,最后的目錄結構如下:
│ manage.py │ ├─app01 │ │ admin.py │ │ apps.py │ │ models.py │ │ tests.py │ │ views.py │ │ __init__.py │ │ │ └─migrations │ __init__.py │ ├─Django_s1 │ │ settings.py │ │ urls.py │ │ wsgi.py │ │ __init__.py │ │ ├─static └─templates
4、 創建一個測試頁面,驗證:
- 主要的業務代碼是放在app01目錄下view.py文件中,先在view.py中寫如下測試代碼:
from django.shortcuts import HttpResponse def index(request): return HttpResponse("index")
- 同時在url.py中添加上對應關系:
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), ]
登錄http://127.0.0.1:8000/index/
5、 寫一個簡單的登錄頁面
這里我們需要記住:
templates目錄中放的是html文件
static中放的是靜態文件即css,以及js文件
views.py中寫業務代碼
urls.py中寫路由關系
首先在templates里寫login.html,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> <p> <input type="text" name="user" placeholder="用戶名"> </p> <p> <input type="password" name="pwd" placeholder="密碼"> </p> <input type="submit" value="提交"> </form> </body> </html>
在views,添加如下代碼:
def login(request): if request.method=="GET": return render(request,"login.html") elif request.method == "POST": u = request.POST.get("user") p = request.POST.get("pwd") if u == "zhaofan" and p == "123": return redirect('/index/') else: return render(request,"login.html") else: return redirect('/index/')
在urls路由關系里添加如下代碼,黃色背景部分:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^login/', views.login), ]
這樣通過瀏覽器訪問效果如下:
關於獲取數據和文件上傳
實現的代碼如下:
1、 在views.py里寫如下函數
def login(request): if request.method=="GET": return render(request,"login.html") elif request.method == "POST": # v = request.POST.get("gender") # print(v) # f = request.POST.getlist("city") # print(f) # file = request.POST.get("upload") # print(file) obj = request.FILES.get("upload") print(obj,type(obj),obj.name) import os file_path = os.path.join("upload_dir",obj.name) f = open(file_path,mode="wb") for i in obj.chunks(): f.write(i) f.close() return render(request, "login.html") else: return redirect('/index/')
2、 login.html文件中代碼如下;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post" enctype="multipart/form-data"> <p> <input type="text" name="user" placeholder="用戶名"> </p> <p> <input type="password" name="pwd" placeholder="密碼"> </p> <p> 男:<input type="radio" name="gender" value="1"> 女:<input type="radio" name="gender" value="2"> </p> <p> 乒乓球:<input type="checkbox" name = "favor" value="1"> 籃球:<input type="checkbox" name = "favor" value="2"> 羽毛球:<input type="checkbox" name = "favor" value="3"> </p> <p> <select name="city" multiple> <option value="sh">上海</option> <option value="bj">北京</option> <option value="tj">天津</option> </select> </p> <p> <input type="file" name="upload"> </p> <input type="submit" value="提交"> </form> </body> </html>
3、 關於上述代碼中知識點的總結:
- 當獲取單個值的時候類似單選框,可以給標簽設置name屬性,然后通過類似request.POST.get("gender")方式獲取相應的值即:
request.POST.get("標簽name屬性值")
- 當獲取多個值的時候類似復選框以及可以多選的select的時候,通過request.POST.getlist("city")的方式獲取相應的內容,這樣得到的是一個列表,即:
request.POST.getlist("標簽name屬性值")
- 當時獲取上傳文件的時候
首先form表單中應該有如下屬性:enctype="multipart/form-data"
然后在views.py中通過obj = request.FILES.get("upload")獲取文件的一個對象,通過打印可以看出這個對象的詳細內容為:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>即:
request.FILES.get("標簽name屬性值")
最后通過如下方式循環將文件接收:
import os file_path = os.path.join("upload_dir",obj.name) f = open(file_path,mode="wb") for i in obj.chunks(): f.write(i) f.close()
這樣最后所有的上傳文件都會放在upload_dir目錄下,當然這個目錄需要自己提前創建
關於FBV和CBV
1、 FBV
FBV對應的是function base view
urls.py
index--->函數名
views.py
def 函數(request)
2、 CBV
CBV對應的是class base view
3、 之前寫的都是FBV的方式多點,下面通過一個CBV的例子來理解CBV
- 首先先寫一個home.html文件,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/home/" method="post"> <input type="text" name="user"> <input type="submit"> </form> </body> </html>
- views.py中寫如下代碼:
from django.views import View # 這里Home需要繼承View class Home(View): def get(self,request): print(request.method) return render(request,"home.html") def post(self,request): print(request.method) return render(request, "home.html")
- urls.py中寫如下代碼高亮部分:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^login/', views.login), url(r'^home/', views.Home.as_view()), ]
- 效果如下
當訪問頁面內容時:
查看django打印的日志可以看出都是的get請求:
當點擊提交的時候:查看django的日志可以看出是post請求;
4、 關於CBV,django在內部是如何實現的?
分析:
當訪問頁面的時候:頭部中請求的方法是GET方式:
當點擊提交的時候:頭部中的請求的方法是POST方式:
其實當發送請求的時候,是將Request URL以及Request Method同時傳遞給django,先匹配url,然后去找對應的類,然后找相應的方法,也就是post或者get等,這里判斷是post還是get是通過反射實現的。
查看view類里面有一個函數:dispatch函數
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
從函數可以看出是dispatch先做了一個反射的功能
所以這個請求的過程是:請求--->dispatch--->get/post
將views.py中的代碼更改為如下:
from django.views import View # 這里Home需要繼承View class Home(View): # 這樣這里就相當於一個裝飾器的功能,可以自己定制化內容 def dispatch(self, request, *args, **kwargs): # 調用父類中dispatch方法 print("before") result = super(Home, self).dispatch(request,*args,**kwargs) print("after") return result def get(self,request): print(request.method) return render(request,"home.html") def post(self,request): print(request.method) return render(request, "home.html")
這樣從效果就可以看出當再次訪問home頁面的時候,每次都需要打印before以及after
5、 模板語言中字典的循環
views.py中寫如下代碼:
USER_DICT = { "k1":"root1", "k2":"root2", "k3":"root3", "k4":"root4", "k5":"root5", } def index(request): return render(request,"index.html",{"user_dict":USER_DICT})
創建一個index.html文件,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for k,row in user_dict.items %} <li>{{ k }}-{{ row }}</li> {% endfor %} </ul> </body> </html>
按照上面的循環方式結果如下:
同樣的字典循環包含以下幾種方式:
user_dict.items ---- 這樣循環row為(k,value)
user_dict.values ---- 這樣循環row為value值
user_dict ----這樣循環的話row為k值
關於URL路由系統
1、 實現一個點擊查看詳情的例子(方法一)
views.py中的代碼如下;
USER_DICT = { "k1":{"name":"root1","email":"root1@qq.com"}, "k2":{"name":"root2","email":"root2@qq.com"}, "k3":{"name":"root3","email":"root3@qq.com"}, "k4":{"name":"root4","email":"root4@qq.com"}, } def index(request): return render(request,"index.html",{"user_dict":USER_DICT}) def detail(request): nid = request.GET.get("nid") detail_info = USER_DICT[nid] return render(request,"detail.html",{"detail_info":detail_info})
index.html文件中的代碼如下;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for k,row in user_dict.items %} <li><a target="_blank" href="/detail/?nid={{ k }}">{{ row.name }}</a></li> {% endfor %} </ul> </body> </html>
detail.html代碼為如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>詳細信息</h1> <h6>用戶名:{{ detail_info.name }}</h6> <h6>郵箱:{{ detail_info.email }}</h6> </body> </html>
urls.py中寫如下代碼高亮部分:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^login/', views.login), url(r'^home/', views.Home.as_view()), url(r'^detail/', views.detail), ]
實現效果為當打開index主頁的時候顯示:
點擊某個選項,則可以顯示詳細信息
2、 實現一個點擊查看詳情的例子(方法二)
urls.py中的代碼如下高亮部分;
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^login/', views.login), url(r'^home/', views.Home.as_view()), #url(r'^detail/', views.detail), url(r'^detail-(\d+).html', views.detail), ]
views.py中的代碼如下:
USER_DICT = { "1":{"name":"root1","email":"root1@qq.com"}, "2":{"name":"root2","email":"root2@qq.com"}, "3":{"name":"root3","email":"root3@qq.com"}, "4":{"name":"root4","email":"root4@qq.com"}, } def index(request): return render(request,"index.html",{"user_dict":USER_DICT}) def detail(request,nid): # return HttpResponse(nid) detail_info = USER_DICT[nid] return render(request,"detail.html",{"detail_info":detail_info})
index.html中的代碼為:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for k,row in user_dict.items %} <li><a target="_blank" href="/detail-{{ k }}.html">{{ row.name }}</a></li> {% endfor %} </ul> </body> </html>
detail.html中代碼不變
最終的效果,點擊詳細頁面之后,地址變為/detail-2.html而不是之前的/detail?nid-2.html:
3、 針對上述的例子,我們將urls.py進行修改:
將url(r'^detail-(\d+).html', views.detail),修改為:
url(r'^detail-(\d+)-(\d+).html', views.detail),
這樣就存在兩個正則,這樣在views.py中獲取到的時候也需要兩個參數,def detail(request,nid,uid),並且這兩個參數和順序有關,第一個參數就是匹配的第一個正則,第二個匹配第二個正則,這樣就會有個弊端,一旦調用函數的時候參數傳遞錯誤,那個真個函數里設計這兩個參數的計算都會出現問題。
所以我們通常用如下方法:
url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail),
這樣就相當於分組,這樣就不會應該順序問題而影響結果
其實在views.py中有一個終極的方法:
def detail(*args,**kwargs),這樣當多個參數的就會傳遞到*args里,當傳遞字典類型的參數的時候就會傳遞到**kwargs
4、 name
name是對URL路由關系進行命名,以后可以根據此名稱生成自己想要的URL
- url(r'^sdfffddddd/', views.index,name="i1"),
- url(r'^sdfffddddd/(\d+)/(\d+)/', views.index,name="i2"),
- url(r'^sdfffddddd/(?P<nid>\d+)/(?P<uid>\d+)/', views.index,name="i3"),
注意:
獲取當前URL
request.path_info
如果def func(request,*args,**kwargs)
from django.urls impore reverse
url1 = reverse("i1") ----這樣就會生成/sdfffddddd/
url2 = reverse("i2",args=(1,2,)) ---生成/1/2/
url3 = reverse("i3",kwargs={"pid":1, "uid":9}) ---生成/1/9
在模板語言中:
{% url "i1" %} ---生成/sdfffddddd/
{% url "i2" 1 2 %} --生成/1/2/
{% url "i3" pid=1 uid=3 %} ---生成/1/3/
5、 路由分發
當有 多個app的時候
當請求來的時候先到項目的urls.py里,這里的配置如下:
from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^app01/',include("app01.urls")), url(r'^app02/',include("app02.urls")), ]
根據不同的app去不同的app下找相應的app級別的urls
這樣就實現了路由的分發
Django的ORM
1、 創建類
根據類自動創建數據庫表
創建類的位置是在你創建的app目錄下有一個models.py,就在這個文件中創建類,寫一個簡單的類的例子:
class UserInfo(models.Model): #django會默認創建一個id列,並且是自增,主鍵 #創建用戶名列,字符類型,指定長度 username = models.CharField(max_length=32) #c創建密碼列,字符類型,指定長度 password = models.CharField(max_length=64)
創建過之后執行:
python manage.py makemigrations
執行這個命令之后,Django會去找models.py文件
因為我們是在app01下的models.py,所以需要在settings.py中進行指定,找到:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
在最后添加"app01",
這樣當執行python manage.py makemigrations,就會找到app01下的models.py從而在app01目錄下的migrations目錄下生成對應的文件,這個文件就是用於創建數據庫表用的,執行過程如下:
D:\python培訓\Django_s1>python manage.py makemigrations Migrations for 'app01': app01\migrations\0001_initial.py: - Create model UserInfo D:\python培訓\Django_s1>
然后執行python manage.py migrate,這樣就會在數據庫中創建表結構,執行過程如下:
D:\python培訓\Django_s1>python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
D:\python培訓\Django_s1>
因為Django默認使用的是sqlite數據庫庫,所以這個時候會在項目目錄下生成一個db.sqlite3的文件,如果想要查看需要第三方程序查看,如果想要用mysql數據庫,則需要更改配置。
更改配置如下:
找打settings.py中的如下內容,並注釋:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
添加如下內容:並配置自己的數據庫信息
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'dbname', 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '', 'PORT': '', } }
由於Django內部連接mysql用的是MySQLdb模塊,而python3中已經沒有了這個模塊,所以需要使用pymysql來代替MySQLdb,配置如下:
在與項目同名的文件目錄下的__init__.py文件中添加如下內容:
import pymysql pymysql.install_as_MySQLdb()
這樣重新執行python manage.py migrate即可
查看數據你會看到如下表:
其中只有一個表app01_userinfo是你自己通過類創建的其他都是django自己創建的,查看app01_userinfo表結構信息:
2、 添加數據
方法一:(推薦這種方法)
models.UserInfo.objects.create(username = "root", password = "123",)
首先在app01下的urls.py中添加如下內容:
url(r'^app02/',include("app02.urls")),
然后在views.py中添加如下內容:
from app01 import models def orm(request): models.UserInfo.objects.create( username = "root", password = "123", ) return HttpResponse("orm is ok")
然后通過瀏覽器訪問http://127.0.0.1:8000/app01/orm/
這樣就在數據庫里添加了一條數據數據
方法二:
- obj = models.UserInfo(username="dean",password = "123456")
obj.save()
然后通過瀏覽器訪問http://127.0.0.1:8000/app01/orm/
這樣就在數據庫里添加了一條數據數據
方法三:
dic={"username":"erric","password":"555"}、models.UserInfo.objects.create(**dic)
return HttpResponse("orm is ok")
然后通過瀏覽器訪問http://127.0.0.1:8000/app01/orm/
這樣就在數據庫里添加了一條數據數據
3、 查詢數據
查詢所有數據
result = models.UserInfo.objects.all()
這樣得到的result通過打印時如下:
<QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
這個QuerySet是Django創建的一種類似列表的格式,這里面每個元素就是數據庫每行的一個對象,這樣我們就能通過循環獲取每行的數據,方法如下:
result = models.UserInfo.objects.all() print(result) for row_obj in result: print(row_obj.id,row_obj.username,row_obj.password) return HttpResponse("orm is ok")
根據條件 查詢數據
result =models.UserInfo.objects.filter(username="root")
這里的filter就相當於sql語句中的where條件
4、 刪除數據
models.UserInfo.objects.delete() 刪除所有數據
models.UserInfo.objects.filter(id=2).delete() 通過條件刪除
5、 更新數據
models.UserInfo.objects.all().update(password=222)
上面是將所有的的密碼都更改,
如果想要根據條件更改則可以:
models.UserInfo.objects.filter(id=2).update(password=888)