常见问题及配置
将常见的一些报错和配置归纳到此处, 方便用到时直接粘贴复制!!
# 常见报错
# 创建Django项目
mac通过pycharm创建时,自动填充的是这样的 -- 'DIRS': [BASE_DIR / 'templates']
导致项目启动不起来!直接报错
'DIRS': [BASE_DIR / 'templates']
TypeError: unsupported operand type(s) for /: 'str' and 'str'
解决方案 -- 将其改为 'DIRS': [os.path.join(BASE_DIR,'templates')]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')],
'APP_DIRS': True, # -- 是否在App里查找模板文件!!
...
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 迁移项目
注意: 切勿通过数据库可视化工具修改表结构.
应该通过修改ORM对应的表,然后进行数据迁移来修改!!
不然手动修改表结构, 而ORM表没改, 调用通过ORM创建的表时, 传入的字段不存在, 会报错.
1
2
3
2
3
# Pychram查看sqlite
- 在pychrm查看sqlite数据库时,下载对应的驱动可能失败,换一个低点版本试试!!
- 注意: 在sqlite数据库添加字段类型为DateField的数据时会转成时间戳,这可能是个bug,不是你的问题.
1
2
2
# 模块导入问题
在pycharm中,模块导入飘红, 并不一定是代码问题!
>> 解决方案: 将 py文件/脚本 里飘红的导入路径, 自动加入环境变量!!
具体操作: 在settings.py文件中,类似于这样操作 -->
import sys
sys.path.append(BASE_DIR)
sys.path.append(os.path.join(BASE_DIR, "apps"))
原理:
导入模块的路径, 需要从环境变量下开始导 -- 绝对导入
导入模块时,模块的查找顺序,会依次从内存、内置模块、"sys.path"环境变量中找.
经验之谈:
1. pycharm应该打开到项目根目录的
2. 可以将文件夹声明为Python的包,也就是需要在文件夹内创建一个__init__.py空文件.
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
举个例子:
djangoProject
├── app01
│ ├── models.py
│ └── views.py
├── djangoProject
│ └── settings.py
└── manage.py
因为django项目的manage.py是入口文件,该文件会先加载settings.py
settings.py里会通过sys模块将项目的根目录(即manage.py所在目录)加载到环境变量中.
So,在views.py中像以下的格式编写导入语句皆是可以的!!
from app01.models import User # - 借助环境变量进行绝对导入
from .models import User # 相对导入
注: 关于包
- 在包内部,尽量使用相对导入!!
好处1 >> 这样的话,不管包移到哪里去,包里面的导入代码都不用改!!
若使用的绝对导入,移动后,绝对导入所在的路径不在环境变量中,就会报错.
好处2 >> 以最短的路径导入,这样可以尽可能有效的避免循环导入!!(因为导入时会执行里面的代码)
- 在包里有文件a和b, a.py导入b.py中的,b.py导入a.py中的,哪怕写成相对导入的格式,也会判定为循环导入哦!!
Q: 我们发现有些第三方包里并没有用相对导入啊!!下载下来为啥不报错呢?
A: 因为将第三方包下载下来后, 第三方包所在路径就加入到环境变量中啦!!!
- 特别特别注意: 使用相对导入的py文件不能作为脚本运行!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 线上线下配置文件
有两个方案
-
-
1
2
3
2
3
# 常见配置 - settings
# 中文和时区
若想将错误信息的显示从英文变为中文.需将配置文件中 LANGUAGE_CODE = 'en-us' 改为 LANGUAGE_CODE = 'zh-hans'.
- 纯净版 + 改一下时区,中文
"""
LANGUAGE_CODE = 'en-us' 英文
LANGUAGE_CODE = 'zh-hans' 中文
"""
LANGUAGE_CODE = 'zh-hans'
"""
datetime.datetime.now() - utc时间 / datetime.datetime.utcnow() - utc时间
TIME_ZONE = 'UTC'
datetime.datetime.now() - 东八区时间 / datetime.datetime.utcnow() - utc时间
TIME_ZONE = 'Asia/Shanghai'
"""
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
"""
影响自动生成数据库时间字段
USE_TZ = True, 创建UTC时间写入到数据库
USE_TZ = False, 根据TIME_ZONE设置的时区进行创建时间并写入数据库
"""
USE_TZ = False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# static
STATIC_URL = '/xxxx/' # 一般为"/static/"
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
优先在STATICFILES_DIRS中找,没找到就去已注册的app下找static目录!
一般性规则:若是多app,每个app的static目录下应该嵌套一层app名字的目录..
eg:模版中使用
{% load static %}
# Django会自动找到配置文件中的STATIC_URL,自动拼接,相当于src='/xxxx/css/bootstrap.min.css'
<img src='{% static 'css/bootstrap.min.css' %}'>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('api/', include("apps.api.urls", namespace='x1')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
<img src='/media/aa.png'>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Django连接mysql数据库
1> 安装mysql,启动mysql服务
以后想使用mysql,mac终端在任意目录下执行三条命令
- source ~/.zhsrc
- mysqlstart (执行命令mysqlstop停止)
- mysql -uroot -p (初使密码 admin1234)
1
2
3
4
2
3
4
2> 可以使用navicat连接mysql服务,连接好后创建一个名为 db
的数据库
3> 在Django配置文件中进行配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db', # -- 库名
'USER': 'root', # -- 公司里不会用root用户
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': '3306',
'CHARSET': 'utf8'
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
4> 安装相关依赖/第三方组件. pip install pymysql
并 进行 猴子补丁!
"""
安装pymysql模块并在app01的__init__.py下添加以下代码.
但很多时候, 也会在 项目根目录/项目名目录/__init__.py下添加以下代码.
"""
import pymysql
pymysql.install_as_MySQLdb() # -- 默认用的是mysqldb,改成pymysql
# 若启动项目后会报错: AttributeError: 'str' object has no attribute 'decode'
# !!!修改源码. 大致位置python3.8/site-packages/django/db/backends/mysql/operations.py
# 不用刻意去找,在pycharm报错的地方直接点进去就行..
"""
if query is not None:
# query = query.decode(errors='replace') # -- 需要修改的地方
query = query.encode(errors='replace') # -- 修改后的样子
return query
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS: 查看Django支持哪些数据库, from django.db.backends import mysql
查看源码,那些文件夹名就是支持的数据库.
不够用,需要的是这些之外的数据库?在源码中将那个数据库的引擎放到这些数据库的同级目录下.
# 数据库连接池
每次操作数据库,都要创建连接,发收消息,关闭连接.. 很浪费资源,所以需要数据库连接池.
pip install pymysql
记得写那个猴子补丁. + pip install django-db-connection-pool
DATABASES = {
"default": {
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'day04', # 数据库名字
'USER': 'root',
'PASSWORD': 'root123',
'HOST': '127.0.0.1', # ip
'PORT': 3306,
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小,Django运行起来,就与数据库建立10个连接
# 在最小的基础上,不够用,还可以增加10个 即:最大20个.
# So,支持20的并发.再多就得等待.等某个连接用完后交还到连接池中.
'MAX_OVERFLOW': 10,
'RECYCLE': 24 * 60 * 60, # 一个连接可以被重复用多久,超过会重新创建,-1表示永久.
'TIMEOUT':30, # 池中没有连接最多等待的时间.单位s.
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# redis
CACHES = {
"default": { # -- ▲ 这里default
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100},
"PASSWORD": "qwe123",
}
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 常用操作
# 测试环境搭建
测试环境的准备: 去manage.py中拷贝前四行代码!然后自己再写两行!
import os
import sys
if __name__ == "__main__":
# -- 通过环境变量DJANGO_SETTINGS_MODULE来告知Django使用哪个配置
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tem.settings")
import django
# -- 若独立运行的python脚本是需要依赖django中的一些配置,那么就需要用到django.setup()
# 运行到django.setup()该行代码才表明测试环境已经准备好了!!
django.setup()
# -- !!所有的测试代码必须都写在测试环境准备好后,否则不生效!
# 也就是写在django.setup()这行代码之后呗!
# ... ... ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ☆ 快速创建新项目
- pychram创建python项目 + 虚拟环境
- pip install django==3.2
pip install djangorestframework==3.12.4
- 1.在项目根目录下 django-admin startproject dig_2 . (有个点哈!!
2.在项目根目录下 python manage.py startapp api (api换成自己的app名字
3.配置django快捷启动!! 注:项目根目录、选到setting.py文件
4.在项目根目录下,创建apps文件夹 将上一步创建的app移到该目录下 + 改一下app.py + 注册app
(eg:'apps.api.apps.ApiConfig', 'rest_framework',)
5.根路由文件里添加路由 eg: path('api/', include('apps.api.urls')),
- 启动项目看有没有问题!!
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
视图的结构.我们采用 views文件夹 + 多个业务文件夹..
比较懒,多个业务文件夹创建和基础代码我让程序来实现..
import os
def create_files(target_folder, folder_name):
"""创建目标文件夹及其下的 Python 文件,并在 routers.py 中写入内容"""
if not os.path.exists(target_folder):
os.makedirs(target_folder)
print(f"创建文件夹: {target_folder}")
files = ['routers.py', 'serializers.py', 'views.py']
for file_name in files:
file_path = os.path.join(target_folder, file_name)
with open(file_path, 'w') as f:
if file_name == 'routers.py':
# 写入特定内容到 routers.py
f.write(f"""from django.urls import path
from rest_framework import routers
from . import views
router = routers.SimpleRouter()
router.register(r'{folder_name}', views.{folder_name.capitalize()}View, '{folder_name}')
urlpatterns = []
urlpatterns += router.urls
""")
elif file_name == 'serializers.py':
# 提取模块路径并写入内容到 serializers.py
module_path = '.'.join(base_path.rstrip(os.sep).split(os.sep)[-3:-1])
class_name = folder_name.capitalize() + 'Serializer'
f.write(f"""from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from {module_path} import models
class {class_name}(serializers.ModelSerializer):
class Meta:
model = models.{folder_name.capitalize()}
fields = '__all__'
""")
elif file_name == 'views.py':
f.write(f"""from rest_framework.viewsets import ViewSetMixin
from rest_framework.views import APIView
class {folder_name.capitalize()}View(ViewSetMixin, APIView):
pass
""")
print(f"创建文件: {file_path}")
def build_include_path(base_path, folder_name):
"""构建 Django include 路径"""
path_parts = base_path.rstrip(os.sep).split(os.sep)
last_three_parts = path_parts[-3:]
include_path = '.'.join(last_three_parts) + '.' + folder_name + '.routers'
return include_path
def update_urls_file(urls_file_path, new_path_str):
"""更新 urls.py 文件的 urlpatterns 列表"""
lines = []
inside_urlpatterns = False
# 读取现有文件内容
with open(urls_file_path, 'r') as f:
lines = f.readlines()
# 写入更新后的内容
with open(urls_file_path, 'w') as f:
for line in lines:
if 'urlpatterns = [' in line:
inside_urlpatterns = True
f.write(line)
f.write(f' {new_path_str}\n')
elif inside_urlpatterns and line.strip() == ']':
inside_urlpatterns = False
f.write(line)
else:
f.write(line)
def create_or_update_urls_file(parent_folder, new_path_str):
"""创建或更新 urls.py 文件"""
urls_file_path = os.path.join(parent_folder, 'urls.py')
if not os.path.exists(urls_file_path):
# 创建 urls.py 文件并写入内容
with open(urls_file_path, 'w') as f:
content = f"""from django.urls import path, include
urlpatterns = [
{new_path_str}
]
"""
f.write(content)
print(f"创建文件: {urls_file_path}")
else:
# 更新现有 urls.py 文件
update_urls_file(urls_file_path, new_path_str)
print(f"更新文件: {urls_file_path}")
def create_files_and_update_urls(base_path, folder_name):
"""主函数:创建文件夹和文件,并更新 urls.py"""
target_folder = os.path.join(base_path, folder_name)
create_files(target_folder, folder_name)
include_path = build_include_path(base_path, folder_name)
new_path_str = f"path('{folder_name}/', include('{include_path}')),"
parent_folder = os.path.dirname(base_path)
create_or_update_urls_file(parent_folder, new_path_str)
if __name__ == '__main__':
base_path = '/Users/dengchuan/Desktop/dig_2/apps/api/views'
folder_name = 'info'
create_files_and_update_urls(base_path, folder_name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# 开启事务代码
1> 在with代码块中写的所有SQL语句都属于同一个事务;
2> 经验之谈: 在事务中,尽量只写sql语句,业务逻辑代码写在事务外面!!否则数据量一大,逻辑一复杂,会出错!!
from django.db import transaction
try:
with transaction.atomic():
pass
# -- sql1
# -- sql2
except Exception as e:
print(e)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# ☆ 剖析源码技巧
- 直接在视图函数的类的get方法中写 self.dispatch,跳转过去即可!!
- APIView类里有很多默认的配置,点击跳转api_settings,可以看到配置名‘REST_FRAMEWORK’
- 认证权限限流的全局和局部配置的配置名可以去APIView类里找
比如: authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
(等号左边是局部的,右边是全局的!!!)
另外,authentication_classes结尾是es,表示是复数,那么其值是多个,局部和全局都用[];
若结尾没有es,局部和全局的其值就是一个字符串!
1
2
3
4
5
6
7
2
3
4
5
6
7
重写方法
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ["id", 'name',"age"]
def get_fields(self): # -- 重写该方法
res = super().get_fields() # -- 利用super,保证源码的正常运行.
print(res) # -- 在此处打印
return res
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
pycharm debug模式打断点.
右键点查找用法