基于 Django 的接口管理平台中遇到的 bugs 及处理


最近又练习了一个基于 django 的小项目,在此过程中也遇到一些小问题,最后通过查找资料和debug等办法解决了,同时也将一些感觉比较好的bug记录了一下。

积跬步至千里,积小流成江海。加油!

代码提交到 Gitee
步骤 markdown


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 环境作为基础:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rj7KRtOm-1593152839254)(other/settings_env.png)]


2. django 项目之始迁移文件

发现在通过 python manage.py runserver 启动服务后,可以访问:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5I8GONJK-1593152839268)(other/django_start.png)]
但是无法访问 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 了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zs6HXjit-1593152839287)(other/admin_page.png)]


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 before pip 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("注册输入有误,请重新输入~")

在注册页面,注册一个用户:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cWf9CKIx-1593152839301)(other/register_test_page.png)]
提交注册数据后,跳转到登录页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IMi8aQ5v-1593152839324)(other/register_login_page.png)]
如果直接填写信息登录,会出现错误,原因是当前网页的 url 并不是真正的登录(login)页面,所以提交的数据不能通过本应该是 login 的路由来发出 get 请求。仔细看页面的路由,依然是 register。

此时填写正确的注册过的用户数据,会返回注册过程的一些数据。(由于刚刚在注册页面注册了信息,数据被写入数据库,然后再在当前的假登录【实际还是注册的页面】进行登录,就相当于用原来的数据重新注册一次,当然这样是不允许的,在后端代码中就防止该行为出现而抛出异常。)

解决办法:使用 redirect 来重定向到真正的登录页面。

# return render(request, 'login/index.html')
return redirect('login:login')

从该定位该 bug 以及解决可知,以后凡是涉及页面跳转的,不仅要考虑前端页面,还有考虑跳转后的路由 url 是否与页面匹配,防止出现类似的问题。


同样的,在登录页面进行登录后进入首页,但是路由 url 并没有改变,刷新 url 后又回到了登录界面,显然是不合理的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGj7IOYy-1593152839329)(other/login_homepage.png)]
涉及的相关代码:

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值