文章目錄
最近又練習了一個基於 django 的小項目,在此過程中也遇到一些小問題,最後通過查找資料和debug等辦法解決了,同時也將一些感覺比較好的bug記錄了一下。
積跬步至千里,積小流成江海。加油!
1. 虛擬環境
最好在命令行中創建虛擬環境,如果用 pycharm 可能不不小心將本地的 Python 環境作爲基礎添加進去。
D:\>python
Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()
D:\>python -m venv env_testing
D:\>
D:\>cd env_testing/Scripts
D:\env_testing\Scripts>activate.bat
(env_testing) D:\env_testing\Scripts>
使用純淨的 Python 環境作爲基礎:
2. django 項目之始遷移文件
發現在通過 python manage.py runserver
啓動服務後,可以訪問:
但是無法訪問 admin 的頁面,報錯如下:
......
File "D:\env_testing\lib\site-packages\django\db\backends\sqlite3\base.py", line 383, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: django_session
[17/Jun/2020 01:08:13] "GET /admin/ HTTP/1.1" 500 198090
需要遷移數據庫:
(env_testing) D:\django_learning\api_testing>python manage.py migrate
Operations to perform:
Apply all migrations: admin, 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 admin.0003_logentry_add_action_flag_choices... 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 auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying sessions.0001_initial... OK
接着就可以訪問 admin 了:
3. mysqlclient error
manage.py@api_testing > startapp login
......
......
File "D:\env_testing\lib\site-packages\django\db\backends\mysql\base.py", line 36, in <module>
raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.
比較好的解決辦法(親測可用):找到引入 pymsql 的文件,然後添加版本信息,只需要一行代碼。
import pymysql
pymysql.version_info = (1, 3, 13, "final", 0) # 解決mysql版本問題報錯而添加的代碼
pymysql.install_as_MySQLdb()
原因:
- Why do I know you are using pymysql? Because 0.9.3 is just the latest version of pymysql.
- Why use pymysql instead of mysqlclient for the project? Because it is easier to install. pymysql does not depend on system libraries, while mysqlclient relies on a series of system libraries such as libmysqlclient-dev.
- Why is mysqlclient difficult to install and Django still uses it by default? Because mysqlclient is faster and performs better. So if your project has high performance requirements, I suggest you remove the compatible code above and install mysqlclient in your project. If you need help during the installation of mysqlclient, please refer to this link: How to install Python MySQLdb module using pip?, and ensure
libssl-dev
has been installed beforepip install mysqlclient
.
其他方法:參考 stack overflow 上的其他建議。
ps:我特意先使用 google 搜索該問題,第一條搜索結果就可以解決該問題,而且還很簡單;然後我去百度同樣搜索,發現很多方法比較複雜,也沒有軟用,治標不治本,還浪費時間。
4. 運行時在 mysql 的 operations.py 中報錯
(env_testing) D:\django_learning\api_testing>python manage.py runserver
......
File "D:\env_testing\lib\site-packages\django\db\backends\mysql\features.py", line 82, in is_sql_auto_is_null_enabled
cursor.execute('SELECT @@SQL_AUTO_IS_NULL')
File "D:\env_testing\lib\site-packages\django\db\backends\utils.py", line 103, in execute
sql = self.db.ops.last_executed_query(self.cursor, sql, params)
File "D:\env_testing\lib\site-packages\django\db\backends\mysql\operations.py", line 146, in last_executed_query
query = query.decode(errors='replace')
AttributeError: 'str' object has no attribute 'decode'
處理辦法:Go to django folder, then go to this path: django\db\backends\mysql
then go to operations.py
and find query = query.decode(errors='replace')
. Remove the line and put query = errors='replace'
def last_executed_query(self, cursor, sql, params):
# With MySQLdb, cursor objects have an (undocumented) "_executed"
# attribute where the exact query sent to the database is saved.
# See MySQLdb/cursors.py in the source distribution.
query = getattr(cursor, '_executed', None)
if query is not None:
# query = query.decode(errors='replace') # zhuyuping modify
query = errors = 'replace' # zhuyuping add
return query
5. 映射html出問題,函數不存在
NoReverseMatch at /register/
Reverse for 'login' not found. 'login' is not a valid view function or pattern name.
templates\login\register.html
:
<div class="margin-top20 text-center">
已經有賬號了? <a href={% url 'login' %}>登錄</a>
</div>
login\urls.py
:
app_name = 'login'
urlpatterns = [
path('', views.LoginView.as_view(), name='login'),
path('forgot/', views.ForgotView.as_view(), name='forgot'),
path('register/', views.RegisterView.as_view(), name='register'),
path('reset/', views.ResetView.as_view(), name='reset'),
]
需要指定app,所以修改爲:
<div class="margin-top20 text-center">
已經有賬號了? <a href={% url 'login:login' %}>登錄</a>
</div>
同理,還有類似的寫法也都是報這個錯誤。
6. register頁面註冊成功後跳轉到login頁面,但是url還是register
class RegisterView(View):
def get(self, request):
return render(request, 'login/register.html')
def post(self, request):
user_register_form = RegisterForm(data=request.POST)
if user_register_form.is_valid():
username = user_register_form.cleaned_data.get('username')
password = user_register_form.cleaned_data.get('password')
email = user_register_form.cleaned_data.get('email')
if not request.POST.get('aggree'):
return translate2json(errno=ResCode.AGGREE, errmsg=error_map[ResCode.AGGREE])
new_user = User.objects.create(username=username, password=password, email=email)
new_user.save()
return render(request, 'login/index.html')
else:
return HttpResponse("註冊輸入有誤,請重新輸入~")
在註冊頁面,註冊一個用戶:
提交註冊數據後,跳轉到登錄頁面:
如果直接填寫信息登錄,會出現錯誤,原因是當前網頁的 url 並不是真正的登錄(login)頁面,所以提交的數據不能通過本應該是 login 的路由來發出 get 請求。仔細看頁面的路由,依然是 register。
此時填寫正確的註冊過的用戶數據,會返回註冊過程的一些數據。(由於剛剛在註冊頁面註冊了信息,數據被寫入數據庫,然後再在當前的假登錄【實際還是註冊的頁面】進行登錄,就相當於用原來的數據重新註冊一次,當然這樣是不允許的,在後端代碼中就防止該行爲出現而拋出異常。)
解決辦法:使用 redirect 來重定向到真正的登錄頁面。
# return render(request, 'login/index.html')
return redirect('login:login')
從該定位該 bug 以及解決可知,以後凡是涉及頁面跳轉的,不僅要考慮前端頁面,還有考慮跳轉後的路由 url 是否與頁面匹配,防止出現類似的問題。
同樣的,在登錄頁面進行登錄後進入首頁,但是路由 url 並沒有改變,刷新 url 後又回到了登錄界面,顯然是不合理的。
涉及的相關代碼:
class LoginView(View):
def get(self, request):
return render(request, 'login/index.html')
def post(self, request):
try:
post_data = request.POST
if not post_data:
return translate2json(errno=ResCode.PARAMERR, errmsg="參數爲空,請輸入")
user_key = ["email", "password", "remember"]
data_dict = {}
for key in user_key:
data_dict[key] = post_data.get(key)
except Exception as e:
logging.info("錯誤信息:\n{}".format(e))
return translate2json(errno=ResCode.UNKOWNERR, errmsg=error_map[ResCode.UNKOWNERR])
login_form = LoginForm(data=data_dict, request=request)
if login_form.is_valid():
return render(request, 'home/index.html') # 問題原因
將 render 改爲重定向:
# login\views.py
if login_form.is_valid():
# return render(request, 'home/index.html')
return redirect(reverse('index'))
# apiwork\urls.py
urlpatterns = [
path('index/', views.ApiView.as_view(), name='index')
]
# apiwork\views.py
class ApiView(View):
def get(self, request):
return render(request, 'home/index.html')
7. as_view()
路由中調用視圖函數 as_view 函數後面需要加括號。
urlpatterns = [
path('index/', views.ApiView.as_view, name='index')
]
view.py是這樣的:
class ApiView(View):
def get(self, request):
return render(request, 'home/index.html')
當前端發起 request 請求時,會報這樣的錯誤:
TypeError at /apiwork/index/
as_view() takes 1 positional argument but 2 were given
Request Method: | GET |
---|---|
Request URL: | http://127.0.0.1:8000/apiwork/index/ |
Django Version: | 2.2 |
Exception Type: | TypeError |
Exception Value: | as_view() takes 1 positional argument but 2 were given |
urlpatterns = [
path('index/', views.ApiView.as_view(), name='index')
]
8. 前端返回數據有問題
9. 小結
通過這個小小的項目的學習練習,也發現自己在前端知識方面存在很多不懂的地方,需要好好彌補一下。不過由於現在練習的兩個項目都是前後端不分離,這已經是落後的潮流了,以後的趨勢是前後端分離,所以準備學一下目前大火的 Vue,正好也有一個前後端分離的項目可以用來練習。
很多模型以及設計方面的東西還是不太理解的,只是照葫蘆畫瓢,不過相比以前剛剛接觸 django 來說,已經是進步很多了。有些 bug 的處理沒有很好地辦法,有的可以通過刪庫後重新遷移文件來解決,但是實際場景中肯定是不可以這樣做的,也就是說很多 bug 的解決沒有找到本質的原因、沒有用最佳的方法解決,需要慢慢練習、向他人學習。
從一個 django 項目的練習中,可以發現哪些東西都要惡補,記錄一下,加入到後續的學習計劃中。
- HTTP
- 數據庫
- django 的官方文檔
- 前端,Vue