Django教程
Django常用项目结构以及cookiecutter-django。
django-extensions: Django扩展库,有很多其他框架有但是Django没有的扩展
Python一直是我最喜欢的语言,在这个寒假打算认真学习一下Python的Web框架。在Django和Tornado之间我选择了前者,没有特别的原因,网上人云亦云的,肯定不会有一方离另一方差很远,我就直接去看了看Github上两个项目的活跃度,所以选择了前者。
应该说Django坚持自己造轮子,确实为开发者节约了不少的时间,我很看重它的扩展功能,packages数量十分丰富。Django采用的是最流行也是我最熟悉的MVC设计模式,虽然在之前的一个PHP(Laravel)项目中也是采用的MVC模式,但一直都没怎么吃透,始终在各层分离的时候不是很清晰,所以也可趁学习Django对MVC的概念进行强化。
Django另一个我特别喜欢的特性就是Application,它与Project的概念不同,一个APP就相当于一个功能模块,一个Project可以包含多个APP,一个APP可以同时被多个Project引用,App增加了代码的复用机会,提高了扩展性和松耦合性,Django中很多的packages都是以APP的形式存在的。
项目搭建
新建项目
django-admin startproject 项目名 .
这样会把当前目录作为一个Django项目,里面已经有一些基本的配置文件:1
2
3
4
5
6
7
8django_test
├── db.sqlite3
├── django_test
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py在项目根目录下新建APP
django-admin startapp APP名称
如果是搭建一个非常简单的应用,那么不使用APP也行,仅需把路由指向目标view就可以了,但是如果要搭建复杂的应用并且需要良好的隔离性,那最好使用APP。同样,使用该命令也会在当前目录下新建一个目录,里面已经包含一些配置文件:1
2
3
4
5
6
7
8django_test/testapp
├── admin.py # 注册models,用于admin管理
├── __init__.py
├── migrations # 数据库迁移
├── models.py # 定义models
├── tests.py # 单元测试
├── apps.py # App的配置类,AppConfig用于存储应用程序的元数据,可以重载父类的ready()方法,用于在Django启动时执行
└── views.py # 视图文件如果添加了APP,那么需要在主配置文件
settings.py
里面的INSTALLED_APPS
里面添加该APP的名称Hello World!
所有入门教程都必须要有一个Hello World! 首先,在APP的视图文件views.py里添加函数,该函数直接返回一个字符串的响应:1
2
3
4from django.shortcuts import render
from django.http import HttpResponse
def hello(request):
return HttpResponse('Hello World!')然后添加URL,在Project目录里的
urls.py
里进行管理,添加hello的url如下:1
2
3
4
5
6
7from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^hello/', 'testapp.views.hello'),
]运行
python manage.py runserver
如果要以daemon的方式在后台运行,可以使用nohup命令nohup python manage.py runserver 0.0.0.0:8000 &
使用它可以打开Django自带的默认Web引擎,可以在http://127.0.0.1:8000
中查看 在测试的时候可以使用该引擎,它不仅轻量,而且在打开后还会自动检测代码的更改,进行自动更新,这样就不用每次对代码变动了都来重启一次
配置项
全局配置
需要注意的是,Django官方并没有默认的分离配置文件的方案。我认为最佳的方式是,建立多个配置文件(仅仅把重要的需要个性化更改的配置分离开,基础的配置仍然是一个,其他使用继承覆盖的方式,基础的settings.py
里面就直接设置为本地的即可),然后在启动的时候指定不同的配置文件即可。python manage.py runserver --settings=prod_setting
配置文件内容
1 | DEBUG = True # DEBUG模式 |
在其他文件访问全局配置项,可以这样访问:
1 | from django.conf import settings |
应用配置
在上面新建的app的目录结构里面又一个apps.py
文件,它存储了应用的元数据,通过继承AppConfig
来配置其属性,可配置的选项如下:
1 | 可配置的属性 |
请求与响应
1 | HttpResponse('字符串', content_type="text/plain") # 指定content_type的响应 |
路由与视图
- url: web访问请求的入口(相当于Laravel里的路由)
- view:应用的逻辑部分,从客户端接收请求,处理并返回数据,一般返回到template模板进行渲染(相当于Laravel里的控制器)
1 | # 定义路由 |
数据库
Django同很多框架一样使用了ORM(Object Relational Mapping,对象关系映射)的方式,每个model类代表一张数据库的表,每一个属性代表其一个字段(这种特性的实现依赖于python的元类)。
多数据库配置,参考django多数据库配置,主要是在Meta里面添加
app_label
进行标识,然后我的建议是app_label直接和数据库名相同,这样就不用单独写配置关系DATABASE_APPS_MAPPING
了方法二:使用单独的查询语句指定单独的数据库
1
UserModel.objects.using(dbname).all()
方法三:使用DB Router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 指定读写规则
class CasaRouter:
def db_for_read(self, model, **hints):
if 'replica1' in settings.DATABASES:
return random.choice(['replica1'])
return random.choice(['default'])
def db_for_write(self, model, **hints):
""" Always return primary """
return 'default'
def allow_relation(self, obj1, obj2, **hints):
"""Django默认是返回的None,None的话在大多数情况是正确的,表示只有当两个记录在同一个库查询的时候才能进行关联查询"""
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
return True
数据表定义
- Django需要每张表都得有一个
primary_key=True
,如果没有指定,那么会默认假设你的表里面有一个id
列,并且是primary_key
ForeignKey
等外键的定义是可以使用model名称字符串的,而不用引入,因为引入经常会因为交叉引入而报错- 自定义的
manager
封装的是一些动态方法,并不是静态方法,是作用于objects
上面的
定义model的文件是project/app/models.py
里面,例如,要定义一张用户表:
1 | fromo django.db import models |
当建立好models过后,执行如下命令就可以在数据库中新建或更新数据表了:
1 | python manage.py makemigrations # 检测所有的model更改,自动生成相应的migrations文件到migrations目录下面去 |
注:如果是有修改的,那么新添加的数据必须要允许null或者设置默认值,否则会报错,这其实是为了保护已经存在了的数据,当然在添加完该字段后把null去掉再更新数据库就可以了。
字段类型
注:Django默认为每张表设置了一个int(11)的自增主键,不需要自己去定义了。
字段通用参数
1 | primary_key = False/True # 是否设置为主键 |
常用类型
1 | # 数字类型: |
数据库SQL操作
要使用model,必须先导入它,例如from app.models import Blog
,一条记录就是一个model类的实例,要获取其字段值,直接用点号访问即可,例如有Blog对象blog,那么可以直接用blog.userName访问其值。
原生SQL
- 任何时候使用
raw
方式查询,都有sql注入的风险,需仔细检查 - 变量使用
%s
的方式进行传递,否则有注入风险。但是这种方式只支持传value,表名、字段名这些都不支持用这种方式传递,否则会在它们两边加上多余的引号等
1 | # 获取原始SQL语句 |
查询记录
model
对象转换为json,但是对于特殊的字段,例如文件字段依然不能正常转换,最好还是自己写个transform
去转换1
2
3
4
5
6
7
8from django.forms.models import model_to_dict
# 方法一
model_to_dict(blog)
model_to_dict(blog, ['id', 'name']) # 可以只取指定的字段
# 方法二
result = django.core.serializers.serialize('json', some_queryset)Blog.objects.all()[3:30]
只取出部分数据,相当于limit,并不会查处全部偶尔使用自定义的查询条件:
Model.objects.extra(where['FIND_IN_SET(1, field)])
get
方法如果找不到默认会报错,可以使用try except
或者使用filter(id="").first()
进行不报错处理,效果差不多
1 | Blog.objects.all() # 获取该表的所有记录,返回的是记录对象组成的列表 |
新增记录
1 | post = Blog(userName="wanghao", userId=12) |
更新记录
1 | Blog.objects.filter(id=1).update(userName="new") # 批量更新 |
删除记录
1 | Blog.objects.get(userName="xiao").delete() |
数据约束
ForeignKey
如果是面向用户的数据最好别在数据库层面使用强制的外键约束,如果仅仅是在model中定义倒没啥
1
2
3
4
5CASCADE # 默认选项,级联删除
PROTECT # 保护模式,删除时候会报错ProtectedError
SET_NULL # 置空,删除的时候,外键字段被设置为空
SET_DEFAULT # 删除时设置为默认值
SET() # 自定义一个值
例如:
1 | # modles.py |
OneToMany(hasMany)
一对多关系,同样使用ForeighKey实现,例如
1 | # 在models.py中定义 |
ManyToManyField
多对多关系,有一种特殊情况,如果需要对这种关系添加额外的字段,可以使用through,添加额外的表来表示,例如,用户一张表,被使用的物品一张表,用户与物品是多对多的关系,但是有时候我们需要记录下用户使用该物品的一些其他属性,比如使用了多少次什么的,这时候就需要给这个多对多关系添加额外的字段来表示,那就需要添加额外的表了,示例如下:
1 | # 有中间表的情况 |
OneToOneField
必须是一对一,而不是多对一或一对多
1 | # 需要注意的是OneToOneField在反差的时候必须保证有数据存在,如果不存在则会报错RelatedObjectDoesNotExist,可以在使用的时候在封装一下 |
OneToDifferentModel(Generic relations)
- 类似于
Laravel
种的多态关联,一个对象可以同时与多个不同的model进行关联
1 | # 例如有一张评论表,可以给商家评论,也能给用户评论 |
分页
- 自带的分页功能有严重的性能问题,是一次性取出所有数据再从中取出某一页的方式,十分不推荐
- 可以自己写分页功能,分两条sql,一条
COUNT
,另外一条则是LIMIT/OFFSET
,惰性执行可以直接写成Contacts.objects.all()[0:20]
,这同样没有取出所有
1 | # 自带的分页功能,一次取出所有 |
虽然contacts是一个Page对象,但是在模板中仍然可以使用for循环对其进行遍历,它其实是一个对象所组成的list。下面是分页按钮html模板例子:
1 | <nav> |
初始化数据
为了方便迁移,让别人使用你的APP,有时候需要为APP里面的表提供demo数据,这时候就需要预先填充一些数据.这里使用Django的fixtures方式填充(Django提供两种填充方式)。使用JSON格式,我们可以首先使用manage.py dumpdata data.json
方式到处原来数据库中内容看看该格式,类似如下:
1 | [ |
我们可以自己按照这个模板新建填充数据,其中pk指的是主键值。当建立好json文件过后,执行python manage.py loaddata data.json
即可导入数据。
Validator验证
Django
自带的一些验证工具是可以直接使用的,例如RegexValidator('^[0-9]*$')(123)
,验证不通过会直接报错ValidationError
日志
Django
定义了Python自带的logging
模块,我们可以在配置里面配置非常个性化的日志处理方式。例如
1 | # 在settings.py里添加如下配置 |
使用时,只需要这样做
1 | import logging |
Template: Django模板
和所有的MVC框架一样,模板功能是必须有的。这里介绍一下Django模板的使用方法。
模板定义
为了方便管理,最好在app的目录下新建templates文件夹用于存放模板文件,然后在project的配置文件settings.py中指明模板文件夹的位置:
1 | TEMPLATES['DIRS']这个变量中添加即可,比如 |
这样,在该app的view中就可以这样使用templates下的test.html模板文件了。例如:
1 | def test(request): |
参数传递
要向模板中传递参数,可以给render添加第三个参数,该参数其实是一个字典,在模板中可以直接使用该字典的key,例如:
1 | return render(request, 'test.html', {'name1': value1, 'name2': value2} ) |
这样,在模板文件test.html中就可以直接{{ name1 }}
来使用name1
的值了。
继承与引用
模板方便之处就是可以使用继承将代码分块并且将重复的地方都写在一个base.html
里。当要实现继承的时候在html文件第一行写上
1 | {% extends 'base.html' %} |
然后分别实现其区块即可。 在base模板中一般这样定义区块:
1 | {% block 块名 %} |
如果子模块没有定义某个block的内容,那么就采用父模板的,如果需要使用父模板的内容可以用{{ block.super }}
模板也可以通过引用其它模板的代码,例如,在要引用的地方使用:
1 | {% include 'nav.html' %} |
静态文件css、js、img
静态文件一般当然是要存放在自己的app里面,这时候,就应该指定静态文件的路径在project的配置文件settings.py
中添加如下配置:
1 | STATICFILES_DIRS = ( |
在模板中使用静态文件就这样:
1 | {% load staticfiles %} |
模板标签
Django内置了一些比较常用又实用的标签:
1 | # 变量 |
过滤器
可以直接格式化输出,是一种最便捷的转换变量输出格式的方式。
1 | {{ today | data: "Y m d" }} # 格式化输出时间 |
这里是常见的过滤器:
1 | add:将该数字加上一个数字,例如 {{ value|add:"2" }},如果原来的值为4,那么新的值就为6,不仅进可以作用与int,还能作用与列表,将列表中每个值都加 |
常用过滤器组合:
1 | {{ user.name.split|join:"_" }} // 将空格转换为下划线 |
其他标签
1 | # autoescape标签 |
DJango Admin后台管理
Django Form表单系统
用户管理功能
扩展/自定义用户表
- 注意扩展用户表必须在项目最开始的时候做,否则在migrate的时候会报错
Django默认通过django.contrib.auth
提供用户认证相关功能,用户表默认为auth_user
,但是如果我们想要使用给自己的用户表用于认证,但是又能用到django原有的认证功能,那么可以这样扩展用户表:
1 | # 在app的models里面建立一个新的model |
用户登录相关功能
1 | # 创建用户,如果是自建的User,那么应该引入自己的User model |
自定义登录验证功能
有时候,我们可能想使用邮箱验证,而不是用户名,那么我们可以自己建立一个认证后台,例如backends.py
1 | from django.conf import settings |
然后在settings.py中添加如下代码:
1 | # 该字段指定了默认的验证后台,从上到下顺序验证,如果上面验证不成功就验证下面的 |
这样,就可以依然使用刚才的代码对用户登录进行验证了。
media文件处理
- 如果要在
template
里使用MEDIA_URL
变量,那么需要在配置文件的TEMPLATES->OPTIONS->context_processors
添加django.template.context_processors.media
media
和static
一样,都只能在DEBUG=True
的时候生效,在线上环境,这两者都是使用的服务器进行代理静态文件- 用户上传的文件一般叫做media,可以在
settings.py
里面添加如下配置定义其目录
1 | MEDIA_URL = '/media' |
- 在model里面,可以直接定义某个字段上传到的路径:
1 | class Post(models.Model): |
Channels
用于与websockets通信
signal
参考django中signal与操作系统的signal是完全不一样的.Django的signal是一种同步的消息队列.通常在以下情况进行使用:
- signal的receiver需要同时修改对多个model时
- 将多个app的相同signal引到同一receiver中处理时
- 在某一model保存之后将cache清除时
- 无法使用其他方法, 但需要一个被调函数来处理某些问题时
- 作为网站的通知
Django部署
自定义存储系统/七牛云存储
Django缓存系统
能够缓存视频或者模板片段或者API。
Django测试
可以使用
python manage.py shell
进入shell终端直接访问项目中的各种对象如果想要在正式的数据库中测试数据,而不是让测试工具自己创建一个新的数据库,可以在
settings.py
中这样指定测试数据库,但是需要注意的是一定要加--keepdb
选项,否则可能删除掉原来的数据库1
2
3
4
5
6
7
8
9
10
11
12
13
14DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test',
'USER': 'root',
'PASSWORD': 'test',
'HOST': '127.0.0.1',
'PORT': 3306,
'CONN_MAX_AGE': 0,
'TEST': {
'NAME': 'test', # 与上面的数据库名相同
}
}
}--keepdb
选项只是防止测试数据库被销毁,但是测试时候执行的其他操作依然都会回滚,如果想要操作数据库不回滚,那么可以在测试类上重载_rollback_atomics
这个方法:1
2
3
4
5
6
7
8
9
10class MyModelTest(TestCase):
def _rollback_atomics(cls, atomics):
"""Rollback atomic blocks opened by the previous method."""
for db_name in reversed(cls._databases_names()):
# transaction.set_rollback(True, using=db_name) # 注释掉这一行即可
atomics[db_name].__exit__(None, None, None)
def test_get_by_id(self):
print(MyModelTest.get_by_id(1))
Django国际化
I18N
表示国际化,L10N
表示本地化。Django使用的是gettext
工具进行国际化的翻译。- 如果编译过后依然不生效,那么把
*.po
里面的fuzzy
删掉,再不行就重启uwsgi
进程
为了实现国际化我们需要这样做:
将需要翻译的字符串在源码中进行标注
1
2
3
4from django.utils.translation import gettext as _
def test(request):
return HttpResponse(_("test"))如果是在模板中,需要这样标记
1
2
3{% load i18n %}
<title>{% trans "test" %}</title> # 字符串必须加引号
<title>{% trans 变量 %}</title>在
settings.py
中配置并新建国际化文本存放目录,即个目录locale
目录1
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]
生成指定语言的文件目录,这条命会扫描Django项目源文件,将其中标记为需要翻译的字符串抽取出来,统一放在
locale/zh_hans/LC_MESSAGES/django.po
这个文本文件里面1
2
3
4支持的语言列表:http://www.i18nguy.com/unicode/language-identifiers.html
python manage.py makemessages -l zh_Hans # 简体中文,需要注意的是mac上面大小写不敏感,但是linux上面会存在问题,语言文件这里必须大写,并且项目中其他地方用zh-hans
python manage.py makemessages -l ja # 日文
python manage.py makemessages -l ko # 韩文文本文件中会列出我们所有标记了的字符串,你可以在每个字符串下面填上对应的值,例如
1
2
3: web/views.py:15 # 这是字符串抽取的来源
msgid "test" # 默认会将字符串放在msgid
msgstr "测试" # 这里的翻译需要自己填写将
*.po
文件编译成*.mo
文件1
python manage.py compilemessages
语言切换相关的方法:
1
2
3
4
5
6
7
8
9# 获取在settings.py的LANGUAGES=()中定义了的语言列表
{% get_available_languages as langs %}
{% for lang in langs %}
{{ lang.0 }} {{ lang.1 }}
{% endfor %}
# 获取当前的用户语言,例如zh-hans
{% get_current_language as LANGUAGE_CODE %}
{{ LANGUAGE_CODE }}
切换语言
Django根据以下顺序去决定应该使用哪种语言
请求的时候手动更改,这种方法仅用于当前请求:
1
django.utils.translation.active('en')
i18n_patterns: 即直接根据url中的语言来判断
1
2# 在url中这样定义,这样,在访问domain/的时候就可以以domain/zh-hans/的方式访问特定语言了
path('<slug:slug>/'))request.session[translation.LANGUAGE_SESSION_KEY],这种方式如果要切换,只需要设置以下即可
1
request.session[translation.LANGUAGE_SESSION_KEY] = language
request.COOKIES[translation.LANGUAGE_COOKIE_NAME]
request.META[‘HTTP_ACCEPT_LANGUAGE’],即http的header头中的
Accept-Language
获取当前语言
1 | request.session[translation.LANGUAGE_SESSION_KEY] # 如果在session有设置可以从session读 |
翻译JS中的内容
要支持这种js,必须得我们自己去修改js,所以在引入第三方库时最好不要引入写死语言的,这种方式更多用于我们自己的js。详细步骤如下:
按照上面的方法在js中进行标记
1
gettext('Next'); // js中直接使用gettext来将字符串标记起来
在根url中添加一个url
1
2
3
4from django.views.i18n import JavaScriptCatalog
path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog')
path('jsi18n/web/', JavaScriptCatalog.as_view(packages=['web']), name='javascript-catalog') # 这种方式仅针对某个具体的app进行js文件的饭翻译每次在引入需要国际化的js之前,先引入这个js文件,这种文件中定义了gettext等几个翻译的工具。
1
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
帮助方法
django.utils.crypto.get_random_string(length=32, allowed_chars='abcd')
: 生成随机字符串
django-cron插件
Django下的定时任务插件,我以前用的是django-crontab
,但是现在觉得django-cron
更好用,好处是每次执行结果在数据库中都能存储,并且cron中要么是laravel
定时任务那样的一条,如果要分开则是带了名字的,不再是一行看不懂的字符。
TroubleShooting
如果想要直接执行_./manage.py_来启动runserver,那么可以修改manage.py文件如下:
1
2
3
4
5
6
7
8#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "frontend.settings")
from django.core.management import execute_from_command_line
sys.argv = ['manage.py', 'runserver', '0.0.0.0:8000']
execute_from_command_line(sys.argv)保存用户上传的图片或文件:使用Django自带的文件存储系统:
1
2
3
4
5
6
7
8from django.core.files.storage import FileSystemStorage
storage = FileSystemStorage(
location = '/var/www/site/upfiles',
base_url = '/upfiles'
)
content = request.FILES['the_file']
name = storage.save(None, content)
url = storage.url(name)Django模板for循环index
1
2
3
4{% for item in item_list %}
{{ forloop.counter }} {# 从1开始的序号 #}
{{ forloop.counter0 }} {# 从0开始的序号 #}
{% endfor %}Django模板对HTML字符串进行转移,如果有一个HTML格式字符串,比如
<strong>haofly</strong>
那么当把它作为一个变量传递到html中区的时候,会原封不动的保留,很明显我们有时候并不想这样,而是真的想将haofly
进行加粗,可以这样做1
{{ variable name | safe}}
**migrate的时候出现类似这样的错误:
django.db.utils.OperationalError: (1091, "Can't DROP 'os_id_id'; check that column/key exists")
**,原因是你在试图创建一个已经存在的column或者删除一个已经删除的column,这时候需要在migrate后面添加一个参数忽略这些:python manage.py migrate —fake
将上传的文件写入到本地,使用chunks()生成器可以将文件一块一块地写入,而不使用read方法,这样可以防止大文件写入失败:
1
2
3
4destination = open('temp/' + filename, 'wb+')
for chunk in file.chunks():
destination.write(chunk)
destination.close()通过Ajax发送多维数组,原生不支持的,不过可以在前端以及后端同时传输JSON格式的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$.ajax(
url: 'test',
datatype: 'json',
data: JSON.stringify({
'one': 123,
'two': {
'two_one': 'test'
}
})
)
# 在后端使用JSON进行解析
def test(request):
data = json.loads(request.POST)
或者前端不变,后端用这个来接收request.POST.getlist('taskIdList[]')POST请求发生403错误:
Forbidden (403) CSRF verification failed. Request aborted.
: 原因是Django默认给所有的post请求都添加了CSRF验证中间件,要想对某个路由(url)忽略,可以使用csrf_exempt,关于CSRF的其它一些装饰器见https://docs.djangoproject.com/en/2.0/ref/csrf/1
2
3
4from django.views.decorators.csrf import csrf_exempt
def webhook(request):
pass如果要保留csrf以提高安全性,那么可以这样做:
1
2// 在前端响应表单处添加以下标签以生成一个隐藏的input字段,然后前端通过js进行获取,在请求时候将它的value放入header头X-CSRFToken即可
{% csrf_token %}ValueError: The database backend does not accept 0 as a value for AutoField. 这是因为把某个外键的默认值设置为了0,但是外键对应的字段确实一个自动增长的键,这种情况要么把默认值设成大于0的,要么设置为允许NULL
TemplateSyntaxError: Could not parse the remainder: 通常原因是模板标签语法有点问题,比如:
1
{% if a=='2' %} # 是错误的,不仅%需要有空格,==两边都得有空格
迁移数据库后即使输入正确的用户名密码也无法进入后台管理: 重设密码,或者清除cookie即可
使用nginx代理静态文件前端静态文件能正常获取,但是管理后台的静态文件都404了: 原因是没有使用
python manage.py collectstatic
命令将所有的静态文件提取到根目录的/static
目录下django.core.exceptions.ImproperlyConfigured: Requested setting DEBUG, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing setting: 需要手动设置
export DJANGO_SETTINGS_MODULE=my_project.settings
,但是如果是下面这种情况依然是不行的DJANGO_SETTINGS_MODULE not working: 在项目还在初始化的时候,我们应该用
django-admin.py
命令,但是项目初始化完成后我们就应该用python manage.py
命令来代替,这样才能正确地找到路径
扩展阅读
- Sasha’s Pony Checkup: 简单的Django网站安全检测