Python Web之Django

安装

pip install django
在命令行下输入:django-admin,若显示其所有指令则说明安装成功

项目基本使用

创建项目
django-admin startproject 项目名
运行项目
python manage.py runserver ip(默认127.0.0.1):端口号(默认8000)

当整个工程启动后,修改文件代码后服务端会自动更新,所以不需要重启服务,但是如果是增删文件之类的操作,就需要重启服务端了。
注:
对于运行的django工程,如果在配置文件(settings.py)中关闭了debug(DEBUG=False),那么访问静态文件就会失败,此时可以在运行命令后面加上--insecure参数即可,举例:

python manager.py runserver --insecure
项目目录

要在IDE下打开该工程直接把整个文件夹导入即可,新创建的项目目录分布如下:

project名
    - project名      #整个工程配置文件都在该目录里面
        -init           #必须要有这个文件,内容为空就行
        -setting        #配置文件
        -url            #url路由关系,整个网站的目录
        -wsgi       #遵循wsgi规范,相当于调用别的socket
    -manage         #管理整个工程,包括启动
常用配置项

1.静态文件路径
setting文件中,有个TEMPLATES列表,在其下的DIRS里加入目录路径,以后该项目就会自动搜索该路径下的文件,比如有一个文件夹abc,abc下有个a.html文件,则把abc加入到DIRS里以后,该项目里面要使用a.html文件时只需输文件名即可,不需输绝对路径,DIRS就相当于windows下的path
如果是配置一些静态文件,比如CSSJS文件,那么就创建一个文件夹叫static,把静态文件放这里面,然后在settings文件加入语句:

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),       #逗号别忘了,否则会报错
)

2.允许访问的域名
ALLOW_HOSTS,如果要想别人能够访问自己,第一步是设置ip为:0.0.0.0,第二步就是在配置里面把允许的域名加入,如果想要所有域名都能访问就设置为:['*', ]
3.数据库
DATABASES里配置
4.时区
https://www.cnblogs.com/brad1994/p/6761110.html
注:
当出现数据库记录时区问题时参考:
https://www.jianshu.com/p/72a015133bdb

url映射

urls文件主要负责各个链接和对应函数的映射关系,所以要添加一个url及对应函数,只需在文件中加入一行内容:path('链接后缀', 链接对应函数或者类下的方法),一般情况下函数和类放在app下的views文件中。(在django2.0之前的版本里是使用url来做路由映射的,其还支持正则语法,但是在2.0版本开始,一般推荐使用path,如果需要用到正则映射,可以使用re_path

示例

urls中:

from django.shortcuts import HttpResponse

def home(request):  #必须要有request,其是访问时所有的请求内容
    return HttpResponse("

aaa

") #返回请求结果内容 path('aaa', home), #当页面后缀是/aaa时,执行home函数
类url映射

一般一个url对应一个函数,用上面的就可以了,但有时候有一组有规律的url都是对应同一个函数,比如/a1.html/a2.html…/an.html都是用abc函数,那么一行行加path('ax.html', abc)未免太麻烦,所以可以用正则表达式来对同一类进行映射,此时需要先from django.conf.urls import url,然后在urlpatterns当中加一行:url('a(\d+).html', abc)即可,但因为有一个正则值,所以执行的函数abc里就不能只有一个参数request,还要再加一个参数来接收正则值,比如nid,如果多个正则,就加多个参数,其是按顺序来传递。

获取当前访问的路由

https://blog.51cto.com/ipcpu/2173824

APP

这里的app不是android程序,而是可以做某些事的web应用,比如该网站的一个投票系统,相当于网站的某一个版块。前面的project是一个特定网站的配置和应用的集合,一个project里可以有多个app,一个app也可以在多个project中。

创建app
python manage.py startapp app名

此时就会新建一个app文件夹,然后界面内容主要在目录下的view文件里写,到时候再到project的url下导入app.view就行了。

APP目录
migration  #数据修改表结构
admin      #自带的后台管理系统
apps       #用来配置当前app
models     #ORM,在里面写指令的类,然后通过命令就会帮你创建对应的数据库结构
tests      #单元测试
views      #业务代码
Views

views文件主要用来编写业务代码,界面显示之类的,所以我们一般在urls文件中导入app的views模块,然后调用views里面的函数即可。里面第一个接收的参数一定是request,并且返回结果必须为HttpResponseBase对象或子对象,即把字符串放到该对象里,展示的一般是返回的html语句,传统有2种方法:
(1)直接将"""str"""包起来的html语句返回,但这样比较麻烦,也显得代码混乱
(2)编写一个html文件,然后用open打开文件读取后,再返回,但一个文件要打开、读取、关闭、返回,也比较麻烦
(3)基于上面的情况,django提供了一个模块用来实现直接将一个文件返回,一种是通过django.template.loader下的render_to_string来渲染,此时会自动在项目的templats下寻找对应文件,举例:

from django.template.loader import render_to_string

def abc(request):
    return HttpResponse(render_to_string('a.html'))

如果显示路径不存在,那么去settings文件下给TEMPLATES-'DIRS'添加'templates'路径,同理,如果想要去别的文件夹寻找文件,可以把路径加入到'DIRS'中,举例:

'DIRS': ['templates', r'e:/'],

此时就会在本地的e盘下寻找了
还有就是'APP_DIRS'项如果设置为True则代表在'DIRS'下如果没找到文件就会去各个app下寻找,其中被寻找的app都是在'INSTALLED_APPS'下存在的,所以配置app下的template路径需要修改两个地方,举例:

INSTALLED_APPS = [
    ...
    'login'
]
TEMPLATES = [
    {
        'APP_DIRS': True,
    }
]

此时就会去app下读取templates路径了,但是这种情况下templates名字是固定,不能修改成别的

(4)另一种通过shortcuts下的render来实现,举例如下:

from django.shortcuts import render     #用这个返回读取的整个文件
from django.shortcuts import HttpResponse       #一般用这个返回请求内容

def home(request):
  return render(request, 'index.html')
  #第一个参数固定,即整个请求信息
  #第二个是读取的文件,一般写绝对路径,这里已经在setting文件中配置过路径
  #所以可以直接写文件名,具体参考前面配置路径

查看render源代码如下:

def render(request, template_name, context=None, content_type=None, status=None, using=None):
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

不难看出该方法实际上就是对render_to_string的一种封装,并且以HttpResponse形式返回
注:
视图函数都是写在views文件中,由于其机制问题,如果定义了全局变量,那么在视图函数里要是设置同名的变量则会报错,此时必须给视图函数中的变量加上global关键字,举例通过临时变量实现访问计数:

count = 1

def index(request):
    global count
    count += 1
    return HttpResponse(count)

参考:https://blog.csdn.net/u011630575/article/details/51011298

路由传参

跟flask一样在路由中加入:<参数名>,即可

调用app路由映射

如果是映射项目下的urls文件路由,那么直接配置路由+视图函数即可,如果是映射app下的urls文件路由,那么需要在项目的urls文件下导入include,然后通过include('app.urls')来配置(但要记住在settings中已经将该app加入至INSTALLED_APPS里),举例:

###app下urls###
from django.urls import path
from . import views

urlpatterns = [
    path('index/', views.abc),
]
###project下urls###
from django.urls import include
#配置所需的函数

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', include('login.urls')),
    #配置login下的urls路由文件,login/index
]
url命名

给urls设置命名后,无论路由地址如何改,路由名称都是固定的,此时就可以根据名称来找路由,比如:login/下执行aaa函数,现在路由换成了login2/,现在重定向要跳转到执行aaa函数的页面,如果按原来就是:redirect('login/'),但是现在换了路由,就跳转失败了,所以可以先给login/命名,然后重定向到命名对应的路由即可。在urls里配置时,加上name参数即可命名,然后需要用reverse来获取命名对应的路由,举例:

###urls1###
urlpatterns = [
    path('login/', views.login, name='login'),
]
###urls2###
urlpatterns = [
    path('login2/', views.login, name='login'),
]

###views###
from django.shortcuts import redirect, reverse

def abc(request):
    print(reverse('login'))
    #对于前面两次修改的urls1和2,分别输出login/和login2/
    return redirect(reverse('login'))
app命名

在开发时往往是很多人分别负责自己的app,所有出现重复路由的可能性很大,比如两个app里都有名为login/的路由,那么如果要跳转到名为login/路由下,就会不知道该跳转到哪一个下,此时就需要给app命名,步骤:
1.在app的urls下新建变量:app_name = app名
2.在reverse里的路由前加上——app名:
举例:

###配置两个app###
urlpatterns = [
    path('index/', include('index.urls')),
    path('login/', include('login.urls')),
]

###app-login-urls###
app_name = 'login'
#定义了命名空间
urlpatterns = [
    path('', views.abc),
    path('login1/', views.xyz, name='login1'),
]

###app-index-urls###
app_name = 'index'

urlpatterns = [
    path('', views.cde),
    path('index/', views.efg, name='login1'),
    #两个app有一个name相同
]
###app-login-views###
def abc(request):
    return redirect(reverse('login:login1'))
    #指定login这个app下的login1

def xyz(request):
    return HttpResponse('aaa')
###app-index-views###
def cde(request):
    return redirect(reverse('index:login1'))

def efg(request):
    return HttpResponse("bbb")
request请求内容

1.request.method:有POSTGET方法,如果要使用POST方法,需要把settings文件下MIDDLEWAREcsrf那行注释掉
2.request.POST.get(name[, default]):获取POST方法传来的值,第一个参数是指的name,第二个参数是如果没有该值就默认的值,比如None,当然第二个参数不要也可以;如果是获取GET方法传来的,就把POST改成GET即可,但记住get方法只能获取到一个值,比如多选框时要传多个值就只能收到第一个,所以要收多个值就用下面的getlist
3.request.POST.getlist(name[, default]):获取多个传来的值,比如checkbox传来的,收到后以列表形式存放,举例:
html中:

选项1
选项2
选项3

views中:

multi_select = request.POST.getlist('multi_select')
print(multi_select)

结果就会在后台输出一个列表,里面有选中的内容

4.request.FILES.get(' '[, ' ']):获取文件,不过在这之前需要在html的form标签里加上:

enctype="multipart/form-data"

返回的对象可以通过name属性可以获取文件名,read()方法来读取字节流,举例:

content = request.FILES.get('aaa')
print(content.read().decode('utf-8'))

read()方法在文件内容大的时候不建议使用,此时可以用chunks()方法可以把文件存到一个生成器当中,然后进行操作,比如把上传的文件复制一份:

obj = request.FILES.get('aaa')  #获取上传的文件
with open(obj, "wb") as f:
   for each in obj.chunks():    #把上传的文件变成生成器
      f.write(each)

更多参考:
https://blog.csdn.net/zahuopuboss/article/details/54891917
https://www.cnblogs.com/liyqiang/p/8716118.html
5.request.body:原始字符串
6.request.META:包含http首部的字典,里面有特别多的数据,其中获取ip地址有两种:request.META.get('REMOTE_ADDR')request.META.get('HTTP_X_FORWARDED_FOR'),一般都是第一种

更多request请求内容参考

https://www.cnblogs.com/scolia/archive/2016/07/01/5633351.html

cookie设置

简单来说设置需要以下几步:
1.创建返回对象,重定向(redirect)、响应头(HttpResponse)、render什么的都行
2.在该对象上使用set_cookie(key, value, time)设置cookie值和过期时间(其中cookie设置中文会容易出现问题,可以在设置前用json转换一下,过期时间以秒为单位,默认session关闭过期)
3.返回该对象
获取cookie:
1.在request.COOKIES下获取即可
举例:

### 设置cookie
response = render(request, "xxx.html")
content = "这是一个cookie"
response.set_cookie("content", json.dumps(content))  # 中文转json
return response

### 返回cookie
content = json.loads(request.COOKIES.get("content", ""))  # json转回原内容

参考:https://www.jb51.net/article/134322.htm
注:
cookie设置中文会出现乱码问题,因此可以使用json模块的dumps()loads()方法解决,参考下面链接:
https://blog.csdn.net/qq_38707580/article/details/79478461

实例:表单post传递数据并显示

urls中:

from abc_de import views        #导入app下的views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('aaa', views.home),        #后缀是aaa执行home函数
]

views中:

from django.shortcuts import render         #读取文件用
from django.shortcuts import HttpResponse   #返回字符串用,比如html代码
from django.shortcuts import redirect       #重定向到别的页面

USER_LIST = [       #用户信息记录,存放传递的数据
   {
      'name': 'aaa', 'email': 'sads',
   }
]

def home(request):
   if request.method == "POST":     #判断为post请求
      user = request.POST.get('username', None) #获取表单的这四个值
      psd = request.POST.get('password', None)
      name = request.POST.get('name', None)
      email = request.POST.get('email', None)
      print(user)       #在后台打印出请求的值
      print(psd)
      if user == "111" and psd == "111":
         new_user = {
            'name':name, 'email':email
         }
         USER_LIST.append(new_user)     #动态新增一条记录
         return render(request, 'index.html', {'user_list': USER_LIST})
         #读取index.html文件,并将用户记录传递过去
   print(request.method)            #可以发现打开页面是get方法,前面那个提交信息是post方法
   return render(request, 'index.html')  #get方法时直接跳到该页面

index.html中:


姓名: 邮箱:
{% if user_list%} {% for row in user_list%} {% endfor %}
{{row.name}} {{row.email}}
{% else %} 没有新数据 {% endif %}
数据交互

Django中,我们可以通过render进行数据交互,比如传入一个列表,然后在html中实现for循环:
Views中:

USER_LIST = [1,2,3,4,5]
return render(request, 'home.html', {'user_list': USER_LIST})
#向html中传入列表,第三个参数是context类型,可以理解为字典

html中:


{% for row in user_list%}   
    {% endfor %}            
aaa

其还可以把Views的数据传到html中,然后在html中输出这些数据,比如:
Views中:

USER_LIST = [
   {
      'username': 'aaa', 'email': 'sads',
   },
   {
      'username': 'arwaa', 'email': 'sadrwes',
   }
]
return render(request, 'home.html', {'user_list': USER_LIST})

html中:


{% for row in user_list%}

    {% endfor %}
{{row.username}} {{row.email}}

响应头

都在django.http

HttpResponse

默认的响应头,直接将数据返回

JsonResponse

会将数据转成JSON格式后返回,举例:

from django.http import JsonResponse

def post(request):
    return JsonResponse({"a": 1})

默认只能传递字典类型,否则会报错,有时候如果需要传递其他类型,则可以设置参数safe=False,举例:

from django.http import JsonResponse

def post(request):
    return JsonResponse([0, 1], safe=False)

模板语言

DTL

Django自带模板语言

渲染模板

通过django.template.loader下的render_to_string来渲染,此时会自动在项目的templats下寻找对应文件,举例:

from django.template.loader import render_to_string

def abc(request):
    return HttpResponse(render_to_string('a.html'))

如果显示路径不存在,那么去settings文件下给TEMPLATES-'DIRS'添加'templates'
同理,如果想要去别的文件夹寻找文件,可以把路径加入到'DIRS'中,举例:

'DIRS': ['templates', r'e:/'],

此时就会在本地的e盘下寻找了
还有就是'APP_DIRS'项如果设置为True则代表在'DIRS'下如果没找到文件就会去各个app下寻找,其中被寻找的app都是在'INSTALLED_APPS'下存在的,所以配置app下的template路径需要修改两个地方,举例:

INSTALLED_APPS = [
    ...
    'login'
]
TEMPLATES = [
    {
        'APP_DIRS': True,
    }
]
模板传参

前面的render有一个参数context可以用来传参,格式为字典格式,举例:

def abc(request):
    context = {
        'username':'111',
        'password':'222',
    }
    return render(request, 'a.html', context=context)

模板文件中接收:

{{username}}:{{password}}

可以看出context上下文会把字典里的变量都传过去,到模板语言中直接使用即可,如果传的是类这样的数据,可以用.来引用,举例:

class A:
    x = 'x'
    y = 'y'
    def xyz(self):
        return self.x+self.y

def abc(request):
    # return redirect(reverse('login:login1'))
    # return HttpResponse(render_to_string('a.html'))
    a = A()
    context = {
        'A':a
    }
    return render(request, 'a.html', context=context)
###模板###
{{A.xyz}}
可以看见结果为:xy

如果是字典、列表这样的数据,也只能通过.来引用,举例:
context = {
        'a':[1,2,3,4,5],
        'b':{'x':'x', 'y':'y'}
    }
###模板中###
{{a.1}}
{{a.2}}
{{b.x}}

可以看到结果为:

2 3 x

但因为只能用.引用,所以假如其中有名称和该数据类型可以调用的方法重名时,会将该方法覆盖,比如对于上面传递的字典b,在模板中为:

{{b.items}}

可以看到结果为:

dict_items([('y', 'y'), ('x', 'x')])

假如现在b字典里加入个items键,如下:

context = {
        'a':[1,2,3,4,5],
        'b':{'x':'x', 'y':'y', 'items':'z'}
    }

模板不变,结果可以看到结果和上面不一样,变成了:z,所以基于这个问题,给参数取名时不要随便取和方法、内置属性重名的

注:
由于vue框架的语法和django模板语言的语法有些相似,将有可能起冲突,解决原因可参考:https://www.cnblogs.com/ibaicha/p/8043489.html

if语句

基本格式:

{% if ...%}
...
{%elif ...%}
...
{%else%}
...
{%endif%}
for语句

基本格式:

{% for ... in ...%}
...
{%endfor%}

其中如果要反向遍历:

{% for ... in ... reversed%}
...
{%endfor%}

其中还有下面一些属性可以用:

forloop.counter     当前下标,从1开始
forloop.counter0        当前下标,从0开始
forloop.recounter       逆序当前下标,到1为止
forloop.recounter0  逆序当前下标,到0为止
forloop.first           是否为第一次遍历
forloop.last            是否为最后一次遍历

使用举例:

{% for i in a reversed%}
{%if forloop.first%}
这是第一次循环
{%endif%} {{forloop.counter}}:{{i}} {%endfor%} 结果为: 这是第一次循环 1:5 2:4 3:3 4:2 5:1 for循环中还可以用empty判断是否内容为空,若为空则执行其后面的语句,所以一般放在最后判断,举例: {% for i in d%} {{forloop.counter}}:{{i}} {%empty%}
循环对象内容为空
{%endfor%}

可以看到结果为:

:
循环对象内容为空
url反转

通过直接给路由或者{%url 'url名称'%}来跳转,前者在项目中经常会变化,所以可能修改成本会相对高,举例:

###app-index-urls###
app_name = 'index'

urlpatterns = [
    path('', views.cde),
    path('index/', views.efg, name='login1'),
]
###模板中###
index1
index1

可以看到两者都跳转到index/index下,但是前者是根据路由跳转,而后者是根据路由的命名(name)跳转,一般路由经常改,但name值一般不会改,所以用后者相对修改成本更低

如果url需要传参时,可以直接在后面加上:参数名=值,多个则用空格分开,举例:

###views###
def efg(request, id):
    r = str(id)
    return HttpResponse(r)
###urls###
app_name = 'index'

urlpatterns = [
    path('index/', views.efg, name='login1'),
]
###模板中###
index1

如果要传入param参数(即request可以读取的),则必须在链接后面添加,举例:

###views###
def efg(request, id):
    r = request.GET.get('username')
    return HttpResponse(r)
###模板中###
index1

此时就可以看到request里接收到了username的值

过滤器

因为DTL模板里不支持调用函数,所以使用上有很大局限性,而过滤器的作用就是为了替代一些基本的函数,下面是一些常用过滤器:

add

把字符串相加,格式:{{value1 | add: value2}},如果是字符或者列表之类的数据,就会拼接起来,举例:

###views###
def test(request):
    context = {
    'x': 1,
    'y': 2,
    'a': 's',
    'b': '3',
    'p': [1,2,3],
    'q': ['4']
    }
    return render(request, 'a.html', context=context)
###模板中###
{{x|add:y}}
{{a|add:b}}
{{p|add:q}}

结果为:
3 s3 [1, 2, 3, '4']
cut

删除指定字符串,相当于replace(char, ''),基本格式:{{value1| cut: value2}},举例:

###views###
def test(request):
    context = {
    'a': 'ab cde',
    'b': 'b'
    }
    return render(request, 'a.html', context=context)
###模板中###
{{a| cut:"c"}}

结果为:
ab de
default

如果不存在的数据,则设置默认值,格式:{{value1|default:value2}},举例:

{{b | default:"a"}}

因为b不存在,结果为a

safe

防止数据发生变化,比如使用echarts时,发送的数据中如引号则会被转义成',因此用这个过滤器则防止了转义的发生,举例:

{{a}}  //数据可能会发生转义
{{a | safe }}  //此时就不会转义了
date

自定义格式化日期,格式:{{value1 | date: format}},举例:

###views###
def test(request):
    from datetime import datetime
    context = {
    'a': datetime.now(),
    }
    return render(request, 'a.html', context=context)
###模板中###
{{a|date:"Y/m/d"}}
#注意冒号右边没有空格

其中一些格式化参数:

Y   年份
m   月份(不足两位前面补0)
n   月份
d   天数(不足两位前面补0)
j   天数
h   12小时(不足两位前面补0)
g   12小时
H   24小时(不足两位前面补0)
G   24小时(不足两位前面补0)
i   分钟(不足两位前面补0)
s   秒(不足两位前面补0)
自定义过滤器

步骤:
1.在settings文件里的INSTALLED_APPS下给加过滤器的app注册下,比如这里要在index下写过滤器:

INSTALLED_APPS = [
    ...
    'index',    #添加该app
]

2.在该app下创建个文件夹,名字为:templatetags,然后在下面创建两个文件,一个是:__init__.py,空文件即可,另一个是要写自定义过滤器的文件,随意取名,这里去:my_filter.py
3.在my_filter.py下导入 注册过滤器需要的:

from django import template

register = template.Library()

4.编写自己的过滤器,最后用装饰器注册该过滤器,举例:

@register.filter    #这里可以加参数,默认name为该函数名称
def mul(value, value2):
    try:
        return str( value1 * value2)    #实现乘法的过滤器
    except:
        return "wrong"

5.在模板文件导入该文件,然后调用该过滤器,举例:

{% load my_filter %}
{{5 |mul:10}}

结果为:50

参考:https://www.jb51.net/article/117719.htm
出错参考:https://blog.csdn.net/qq_40326481/article/details/80342480

继承和block

像新闻之类的网站的新闻页基本页面结构都相同,如果说每个页面都去重复写这个结构语句显然太浪费,因此就可以写一个专门的模板文件给这些新闻页继承,然后在里面的某个版块写各自的新闻即可,这里要实现这种情景需要执行以下几步:
1.写一个模板文件xxx.html
2.在模板文件可以被添加内容的版块写上{% block 版块名 %}{% endblock %}
3.在要继承该模板的文件里写上{% extends '模板名' %},然后通过语句:{% block 版块名 %}添加在版块的内容{% endblock %},来往版块里添加自己的内容
4.模板文件的版块里可以写内容,文件继承后对应版块不会显示该内容,如果想要该内容可以通过{{block.super}}来获取(这里和jinja2不一样,jinja2是{{super()}}来获取)
举例:

###### 模板文件b.html ######



    模板文件


这是父模板的内容


{% block main %}

这是父模板块1的内容

{% endblock %} {% block main2 %}

这是父模板块2的内容

{% endblock %}
###### 继承模板的文件 ######
{% extends 'b.html' %}

{% block main %}
{{block.super}}
{% endblock %}

{% block main2 %}

这里是子模板的内容

{% endblock %}

可以看出模板文件新建了两个版块:main和main2,然后其他文件继承他,并在这些版块里写内容,其中版块1通过super()继承了模板文件的内容,版块2没有继承内容,最终页面结果为:

...
    模板文件
...

这是父模板的内容


这是父模板块1的内容


这里是子模板的内容2

加载静态文件

1.确保settings文件的INSTALLED_APPS下有'django.contrib.staticfiles'
2.在settings里加一行:STATIC_URL = '/static/'(一般默认也有),即请求静态文件的url
3.在settings文件的INSTALLED_APPS下加入要载入静态文件的app名
4.在app下新建一个static文件夹,把文件放入该文件夹下,比如一个图片a.jpg
5.在模板文件里加载,路径为:/static/a.jpg,或者用{% static '要加载的文件'%}来加载,举例:



上面基本的加载静态文件已经完成了,但是有两个问题:
(1)一个是假如有多个app,而且图片都为a.jpg,那么static/a.jpg到底该指向哪一个文件,虽然说肯定是按配置项里app的顺序来匹配,但如果想要加载别的图片怎么办?这里建议在每个app/static下再创建个文件夹取名为app名,这样地址就是:static/app名/a.jpg,就可以区分开来了。
(2)另一个是前面的文件都是放在app的static下,如果不想放在app下该如何做?可以在settings里加入配置:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

然后在项目下创建一个static文件夹,加载时使用即可加载了

jinjia2

Django也支持使用该模板语言,但是兼容性会弱一点,具体参考flask文章:
https://www.jianshu.com/p/47d7e0c10fef

中间件

Django中的中间件可以理解成flask中的钩子函数、Java里的过滤器那样,是专门在web指定生命周期中执行的函数,在Django配置的中间件默认对于所有的路由都会生效,因此可以通过中间件来配置一些如登录状态判断、IP黑名单等功能,由于中间件会对全局都影响,所以配置过多中间件可能也会造成性能上的问题

使用场景
  • 判断用户是否登录(token校验)
  • IP黑名单(限制IP访问、频率)
  • 配置跨域响应头(统一解决前后端分离时的跨域问题)
自定义步骤

使用中间件需要执行以下步骤:

  1. 自定义中间件:首先新建一个类,并继承于django.utils.deprecation下的MiddlewareMixin类,主要有5个钩子函数,如下面代码:
from django.utils.deprecation import MiddlewareMixin

class My_Middleware(MiddlewareMixin):

    def process_request(self, request):
        print("第一个执行:在请求进来时执行")

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("第二个执行:在匹配到路由,执行视图函数前")

    def process_exception(self, request, exception):
        print("只有在出异常的时候执行")

    def process_template_response(self, request, response):
        print("当视图函数执行完,并且返回的响应对象里必须有render方法时执行")
        return response

    def process_response(self, request, response):
        print("最后执行:在返回响应时执行")
        return response
        # 最后记得返回请求,否则报错
  1. 配置中间件:当编写完自定义中间件以后,需要在settings.py文件的MIDDLEWARE列表里面配置使用的中间件,可以看出在列表中已经有很多Django提供的内置中间件了,如一些Session相关的、Csrf相关、权限相关的中间件,而我们只需要把自己前面编写的中间件的路径和类加入即可,例如前面写的类假如在项目根目录的middleware.py里,那么在MIDDLEWARE里添加如下:
MIDDLEWARE = [
    # 原来的中间件
    ...
    'middleware.My_Middleware'
    # 新添加的中间件
]
  1. 访问如下代码那样一个简单的路由:
def test(request):
    return HttpResponse("test")

可以看到后台输出如下:

第一个执行:在请求进来时执行
第二个执行:在匹配到路由,执行视图函数前
最后执行:在返回响应时执行
中间件执行顺序

process_requestprocess_view中都是按照settings.py里中间件的定义顺序执行,另外三个则是按照settings.py里中间件的定义顺序的逆序执行

更多中间件参考

https://www.jianshu.com/p/caec96cfe53e
https://www.cnblogs.com/buyisan/p/8557252.html

数据库

sqlite

Django默认使用sqlite,如果要修改,则去settings下有个DATABASES里配置,上面2行有配置说明链接,然后其是通过在app的models下创建类(类名就是表名),然后里面在写语句创建列,一般变量名就是列名,不过也可以加db_column参数来设置列名。

创建实例

settings中:
INSTALLED_APPS里添加app的路径,使得其能够认出要执行的是哪个app下的models
models中:

from django.db import models  #导入db操作模块

class User(models.Model):  #表名跟类名一样是User
   username = models.CharField(max_length=32)
   #列名是username,且生成的是char型,最大长度为32,里面还可以加第二个参数null,若为True则允许为空。
   #IntegerField-int型,URLField-链接的str型,BooleanField-bool型…
   password = models.CharField(max_length=32)
   #上面只写了创建username和password列,但其默认还会创建一列id来作为主键,对新数据自增1
   #如果不想默认创建,可以自己定义取名一个自增列,只要加一行:uid = models.AutoField(primary_key=True)
   #那么就会新建一列uid,自增且为主键(必须要是主键)
   my_email = models.EmailField(max_length=64, db_column='email', null=True)
   #这句生成的类名是email,因为设置了db_column属性,并且还设置了允许为空

命令行中:

python manage.py makemigrations
#会生成创建数据库表的文件在migrations文件夹下
python manage.py migrate
#执行前面的文件,创建表列
使用实例

对数据库进行增删改查等基本操作时,则在appviews下通过函数进行操作即可,在这里需要导入对应app下的models,增删改查示范如下:
views中:

from abc_de import models
def opertation(request, nid):
   ########增########
   models.User.objects.create(username = 'root3', password = '666')
   #对User类进行操作,增加一行username=root3&password=666的数据
   obj = models.User(username = 'root2', password = '111')
   obj.save()      #这两句合起来效果和上面那个一样,都是增加数据

   #######删#########
   models.User.objects.filter(username='root').delete()
   #删除数据,不用filter()就把所有数据删了
   
   ########改#########
   models.User.objects.all().update(password='666')    #对所有数据密码改成666
   models.User.objects.filter(username='root2').update(password='777')  #修改对应数据密码

   ########查########
   result = models.User.objects.all()  #查询到所有数据
   for row in result:
      print(row.id, row.username, row.password)
   found = models.User.objects.filter(username = 'root3', password = '666')
   #查找对应条件的数据
   print(found.count()) #查询到的行数
   for each in found:
      print(each.id)
   return HttpResponse('ok')

其他数据库

配置

settings下的DATABASES里进行所有的数据库配置,这里拿mysql举例:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   #mysql引擎
        'NAME': 'shop', #数据库
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}

要注意的是在linux下使用时还需要在项目的__init__.py里加上下面内容:

import pymysql
pymysql.install_as_MySQLdb()
# 添加pymysql驱动
sql操作

步骤:
1.导入操作接口:from django.db import connection
2.直接通过mysql驱动操作(这里是用pymysql
举例:

###views###
from django.db import connection
def test(request):
    cursor = connection.cursor()
    cursor.execute('select * from clothes')
    result = cursor.fetchall()
    context = {
    'result': result
    }
    return render(request, 'b.html', context=context)
###模板中###
{{result}}
orm操作

不需要使用sql语句操作,从而避免了sql注入的风险,且具有易用,性能损耗少,个性灵活,移植性强等特点,在orm中一个类(继承于models.Model)就是一张表,一个属性就是一个字段
步骤:
1.在settings下将要使用orm模型的app添加到INSTALLED_APPS
2.在models文件下创建一个类继承于models.Model(默认用该文件,如果要自己写文件,记得导入相关接口:from django.db import models
3.在类里编写字段属性
4.命令行到项目目录下输入:python manage.py makemigrations
5.命令行输入:python manage.py migrate(linux环境下若报错参考:https://blog.csdn.net/LHYzyp/article/details/70550683)

常用字段属性
AutoField       自增整型
BigAutoField    64位自增整型
BooleanField    bool型
FloatField      浮点型
IntegerField    整型
BigIntegerField 大整型
CharField       char型
TextField       比char型能存更多内容
DateField       日期类型
DateTimeField   日期时间类型
TimeField       时间类型
EmailField      邮箱类型,要注意的是这个只能在表单里进行验证,数据库里只相当一个char型
FileFIeld       文件类型
ImageField      图片类型
ForeignKey      外键
字段参数
# 通用
primary_key  主键
max_length   最大长度
null         是否允许空
unique       是否唯一(索引)
default      默认值
db_index  普通索引
choices  实现枚举字段,传入一个元组,元组里是多对元组,第一个代表数据库中的具体数值,第二个代表显示值

# 时间字段特有
auto_now_add  创建记录时自动添加时间
auto_now  更新记录时自动更新时间

# 外键
to  关联表
to_field  关联字段,默认关联表主键
related_name  反向操作时代替'表名_set'的名称
on_delete  准备删除关联表数据时,关联数据及当前数据的行为
# models.CASCADE-关联数据和当前数据一起删除;
# models.DO_NOTHING-取决于当前默认;
# models.PROTECT-不允许关联表数据删除;
# models.SET_NULL-当前数据字段设置为空;
# models.SET_DEFAULT-当前数据字段设置为默认值;
# models.SET-当前数据字段设置为自定义值
元信息

可以设置Meta类,用于设置元信息,参考:

db_table  表名
index_together  联合索引
unique_together  联合唯一索引
ordering  默认排序字段
简单示例
###models###
class Buy(models.Model):
    id = models.AutoField(primary_key=True)
    #自增主键
    name = models.CharField(max_length=100, null=False)
    #char形非空
    price = models.FloatField(null=False, default=0)
    #浮点
    TYPE = (
        (0, "A类"),
        (1, "B类")
    )
    type= models.IntegerField(choices=TYPE )
    # 枚举

输入步骤4的命令后会在对应app下生成initial文件,输入步骤5后会在数据库生成表,因为app名为index,所以生成的表名为index_buy,其中还生成了很多其他的配置表,如果要自定义生成的表名,就可以在里面加个Meta类,然后设置db_table的值,举例:

class User_test(models.Model):
    username = models.CharField(primary_key=True, max_length=20)
    password = models.CharField(max_length=32, null=False)

    class Meta:
        db_table = "users1"
        #定义表名为users1
更多示例
# 车辆平台部分
# 用户
class UserInfo(models.Model):
    User_id = models.AutoField(primary_key=True)
    #自增主键
    User_name = models.CharField(max_length=20, unique=True, null=False)
    #索引
    User_pwd = models.CharField(max_length=32, null=False)
    User_phone = models.BigIntegerField(null=False, unique=True)
    #大整数
    User_date = models.DateTimeField(null=False)
    #日期时间

    class Meta:
        db_table = "users"
        #表名

# 车辆
class Vehicle(models.Model):
    Veh_id = models.AutoField(primary_key=True)
    Veh_User = models.ForeignKey(to=UserInfo, related_name='Veh_User', on_delete=models.CASCADE)
    #会生成名为Veh_User_id的字段,是users表主键(id)的外键
    Veh_name = models.CharField(max_length=45, null=False)
    Veh_status = models.IntegerField(default=0)
    Veh_mile = models.FloatField(default=0)
    #浮点数

    class Meta:
        db_table = "vehicles"
        unique_together=("Veh_User","Veh_name")
        #联合主键,用户车名不能重复
更多使用参考

https://www.cnblogs.com/study-learning/p/9969477.html
https://blog.csdn.net/kan2016/article/details/82855158
https://www.cnblogs.com/study-learning/p/9969486.html#autoid-1-0-0

1.导入模块下的数据表类
2.视图函数中实例化类,并传入数据
3.通过save()插入数据
举例:

###index-views下###
from index.models import Buy
def test(request):
    buy = Buy(name='fish', price=6.66)
    #增加数据
    buy.save()
    #插入数据到数据库
    return HttpResponse("success")

要注意的是上面那种如果数据已经存在,则会被覆盖,如果是要插入新的数据则用.b=objects.create()方法,举例:

###index-views下###
from index.models import Buy
def test(request):
    buy = Buy.objects.create(name='fish', price=6.66)
    #增加数据
    buy.save()
    #插入数据到数据库
    return HttpResponse("success")

通过objects方法查找,可以通过get筛选器根据主键或id查询返回数据;或者filter筛选器根据条件查询;或者all筛选器返回所有数据(无条件查询),其中filter筛选器可以通过下面的过滤函数选择返回结果:

all()       以列表返回所有数据
first() 返回第一个,没有就None
get()   查找主键对应行
count() 返回查询结果数量
order_by()  根据某字段进行排序,默认从小到大排,字段前面加负号则从大到小排

举例:

def test(request):
    buy1 = Buy.objects.get(pk=5)
    #查找primary_key=1的数据,改成id=5也一样
    print(buy1)

    buy2 = Buy.objects.filter(name='fish').first()
    #查找name为fish的数据
    # print(buy2)

    buy3 = Buy.objects.all().order_by("-xxx")
    #根据xxx逆序返回所有数据
    print(buy3)

    return HttpResponse(str(buy1))

筛选器返回的是一个对象,用all()之类返回多个值的结果则是一个个对象组成的结果集,所以直接输出的结果为:

Buy object (5)
Buy: Buy object (1)
, ]>

所以如果想输出好看点,可以在数据模型里设置一个__str__魔法方法,即在输出时自动调用的方法,里面设置我们希望返回的格式,举例:

class Buy(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100, null=False)
    price = models.FloatField(null=False, default=0)
    
    def __str__(self):
        return "result:(%s, %s, %s)" % (self.id, self.name, self.price)

此时返回结果如下所示:

result:(5, a3, 3.0)
result:(1, fish, 6.66)
, ]>

如果希望获取的不是一整个对象,而是指定的几个字段,可以在后面加上.values('字段1', '字段2', ...),举例:

buy2 = Buy.objects.filter().all().values('name', 'price')
# 获取所有信息的name和price字段
更多查询操作

1.__exact:相当于=,举例:

buy2 = Buy.objects.filter(name__exact='fish').all()
#查找name=fish的

2.__iexact:相当于like,不过貌似无法用%进行模糊查询,所以几乎可以当做和前面的一样,举例:

buy2 = Buy.objects.filter(name__iexact='fish')

3.__contains:相当于模糊查询的like,大小写敏感,举例:

buy2 = Buy.objects.filter(name__contains='A')

等价于:SELECT ... FROM ... WHERE name LIKE BINARY %A%
4.__icontains:和上面唯一的区别就是忽略大小写
更多参考:https://blog.csdn.net/xiecj_2006/article/details/43087137
5.反向查询,比如要查询id不等于1的,那么输入id!=1,肯定是不行的,因为这不是一个条件,而是一个bool对象,所以这里就需要用到一个django.db.models下的Q对象(用第5条的exclude方法也可以),举例:

def test(request):
    from django.db.models import Q

    buy1 = Buy.objects.filter(Q(name='a0'))
    #取name为a0的数据
    buy2 = Buy.objects.filter(~Q(name='a0'))
    #取name为a0的数据然后取反
    print(buy1)
    print(buy2)
    return HttpResponse("success")

6.排除数据,用exclude可以把某些数据排除掉,举例:

buy1 = Buy.objects.exclude(name__contains='a')
#排除所有name包含a的数据

7.链式调用,即查询时支持多重筛选,举例:

buy1 = Buy.objects.filter(name__contains='a').filter(~Q(name='a0'))
#取包含a的数据,再从里面取除了a0外的数据

8.大小比较、模糊查询、包含不包含等
参考:https://www.cnblogs.com/0bug/p/8033898.html

删除只需要在查询的基础上,在后面加一句delete即可,举例:

def test(request):
    buy = Buy.objects.get(pk=12)
    buy.delete()
    #将primary_key=12的数据删除

    return HttpResponse("success")

基于查询的结果,将其当做一个类,直接修改里面的属性,然后通过save保存修改,举例:

def test(request):
    buy = Buy.objects.get(pk=10)
    buy.name = 'buff'
    buy.save()

    return HttpResponse("success")
排序

可以通过order_by实现排序,举例:

buy1 = Buy.objects.order_by('name')
#根据name字段从小到大排序
buy2 = Buy.objects.order_by('-name')
#上面的逆序排序
buy3 = Buy.objects.order_by('name', 'id')
#根据name和id字段排序,以name为主
反向查询

例如有Shop和Book表,每本书有Shop的外键,代表这本书属于哪家店,那么

  • 正向查询就是:通过书知道他属于哪家店
  • 反向查询就是:通过书店找到里面的所有书

也就是查找某个表中所有外键是当前字段的数据,默认通过表类名的小写_set获取,举例:

Shop.objects.filter(id=1).first().book_set.all()
# 找到id为1的书店里的所有书
一对一

一对一就是一张表中的一条数据和另一张表中的数据必须一一对应,不允许有多对一的关系。例如有Shop表和Book表,假如一本书可以出现在很多店里,那么就是多对一;如果只能出现在其中一个店里,那么就是一对一的关系
注:
要实现一对一,首先要进行两表关联,因此需要使用到外键,而外键是一种多对一的关系,如果要实现一个外键只能对应一个值,那么就可以通过唯一索引约束实现,因此一对一的本质是外键+唯一索引约束

获取sql语句

对于orm操作,如果想获得对应的sql语句,可以通过调用其下的query来获取,举例:

buy2 = Buy.objects.filter(name__exact='fish').all()
print(buy2.query)
#获取对应sql语句

要注意的是query只能在QuerySet对象下使用,而像get()filter().first()获取的都是一条数据,因此这种情况下调用query就会报错

上面的方法对于获取单个数据时就无法知道sql语句了,因此还有另一种方法能够获取sql语句,步骤为:
1.from django.db import connection
2.通过connectionqueries获取所有执行了的sql语句
举例:

def test(request):
    from django.db import connection
    buy2 = Buy.objects.filter(name__contains='A').first()
    # print(buy2.query) #单个结果,无法执行,会报错
    print(connection.queries)

    return HttpResponse("success")
聚合函数

即一些django内置的操作数据库的函数,能够代替如count之类功能的函数,这些函数都在django.db.models下,并且不能够单独执行,需要放在一些语句下才能执行,使用举例:

def test(request):
    from django.db import models

    buy = Buy.objects.aggregate(models.Avg('price'))
    #计算平均值的聚合函数Avg
    print(buy)
    return HttpResponse("success")

结果为:
{'price__avg': 6.221666666666667}

可以看到结果输出是个字典,如果想自定义键名可以给聚合函数前加参数名,举例:

buy = Buy.objects.aggregate(avg=models.Avg('price'))
可以看到结果变为:
{'avg': 6.221666666666667}
序列化

可以将model序列化成对应字典数据,举例:

from django.core import serializers
serializers.serialize('json', User.objects.all())
# 第一个参数时序列化类型,第二个参数传入一个迭代器

日志

参考:
https://blog.csdn.net/Jamin2018/article/details/78854566
https://www.cnblogs.com/luohengstudy/p/6890395.html

流程示范

1.创建Project
2.cdProject下创建app
3.配置静态文件路径,在settings文件下加入:

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),       #逗号别忘了,否则会报错
)

4.配置模板路径,在settings文件的TEMPLATES列表下的DIRS里加入路径
5.将settingsMIDDLEWAREcsrf注释掉
6.在urls里定义路由规则,即加入不同后缀链接执行的函数或者对应类的方法,函数的话直接写views.函数名即可,类的话写:views.类名.as_view()
7.在app下的Views里写urls中需要执行的视图函数或者类,函数的话其必须要传参:request,其中函数返回值有三种:
(1)HttpResponse('str'):返回字符串
(2)render( request, 'html路径'):html模板
(3)redirect('url'):url链接,括号里只能填url地址,当然如果要跳转到本地网站的某个后缀下的话,可以加/后缀名,比如要跳转到127.0.0.1/aaa,那么就填/aaa
如果是类的话则需要from django.views import View,然后类继承View,并且类里面写方法,常用的就是postget请求时执行的方法:def get(self, request)def post(self, request),此时django会自动根据请求方式来执行对应方法
8.渲染静态页面,比如导入CSSJS,通过render传值和取值({{}}包着),然后通过django里自带的模板语言实现在html中执行forif等语句({%%}包着)

其他

一些配置
APPEND_SLASH=True
# 若访问的url后面没有/,将自动补全/
获取配置文件settings中的信息
from django.conf import settings

# 视图函数
def xxx(request):
  print(settings.AAA)
  # 获取配置文件settings中的AAA的值
  ...
mysql版本问题

可以在__init__文件中修改mysql版本,举例:

import pymysql
pymysql.version_info = (1, 4, 13, 'final', 0)
pymysql.install_as_MySQLdb()
Django项目中导入模块路径问题

由于在django中默认的启动路径是项目根目录,因此导入相对路径的文件时,例如当前目录下的a.py文件,需要通过from .a import xxx导入,即:相对路径下的文件需要指明以相对路径方式导入
原因:import a导入模块的本质是从sys.path下的路径中寻找对应模块导入,一般执行某个文件时,会将当前文件的路径导入,但django环境下导入的是根目录的路径,因此会提示无法找到对应模块

FBV/CBV

即基于函数的视图开发和基于类的视图开发,前面的示例中视图开发基本都是基于FBV,FBV使用步骤如下:

  1. 在views中编写视图函数,举例:
def test(request):
    if request.method == 'GET':
        return HttpResponse("get请求")
    elif request.method == 'POST':
        return HttpResponse("post请求")
    else:
        return HttpResponse("请求方法不允许")
  1. 在url文件中配置路由映射函数:
urlpatterns = [
    path('test/', views.test),
]

可以看到对于各种方法的请求都在视图函数中用条件判断来进行处理,这种开发的方式本质就是通过指定路由匹配,调用对应的视图函数。而使用CBV的话使用步骤如下:

  1. 编写视图类,需要继承于django.views下的View类,举例:
from django.views import View

class Test(View):
    def get(self, request):
        return HttpResponse("get请求")

    def post(self, request):
        return HttpResponse("post请求")
  1. 在url文件中配置对应的视图类,并调用类下的as_view()方法,举例:
urlpatterns = [
    path('test/', views.Test.as_view()),
]

该方式只需要新建一个继承于View的类,并定义对应的请求方法即可,在View类的内部可以看到有这些关键代码:

http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
...
def dispatch(self, request, *args, **kwargs):
    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)

可以看出其是基于反射实现的对应方法调用,所以我们只需要定义对应的请求方法名即可,假如想要自己定义允许请求的方法名,也可以重写http_method_names

响应头类型

前面的视图基本都是返回HttpResponse响应头,实际上还有很多内置的响应头类型,都在django.http.response下,例如接口开发时常用的JsonResponse,此时返回数据时就不需要我们自己特地使用json.dumps()转成字符串了(其实内部也就是用json.dumps()实现的,不过我们使用它封装好的就可以自己少写些代码)

CSRF攻击防范

在Django中内置了防范Csrf攻击的中间件,此时所有的页面post请求都需要经过token认证才能访问,但并不是所有都有这种需求,所以如果我们不希望全局使用csrf防范时可以有下面几种方式处理:

  • FBV开发时:
  1. 使用csrf中间件,对于不需要csrf机制的路由,添加django.views.decorators.csrf下的csrf_exempt装饰器,举例:
@csrf_exempt
def test(request):
    return HttpResponse("test")
  1. 禁用csrf中间件,对于需要csrf机制的路由,添加django.views.decorators.csrf下的csrf_protect装饰器,举例:
@csrf_protect
def test(request):
    return HttpResponse("test")
  • CBV开发时:
    和FBV一样添加对应的装饰器,但是需要导入django.utils.decorators下的method_decorator装饰器,然后在dispatch方法中,或者视图类上绑定该装饰器,并指定添加的装饰器和对应的方法,举例:
@method_decorator(csrf_protect, name='dispatch')
# 指定在dispatch中添加csrf机制,此时除了get方法都将有csrf机制
# @method_decorator(csrf_protect, name='post')
# 只指定post方法添加csrf机制
class Test(View):
    def get(self, request):
        return HttpResponse("get请求")

    def post(self, request):
        return HttpResponse("post请求")

csrf中间件使用参考:
https://blog.csdn.net/qq_41000891/article/details/82784489
https://blog.csdn.net/wanglei_storage/article/details/85166942

更多csrf中间件参考:
https://www.cnblogs.com/sui776265233/p/9664642.html
https://blog.csdn.net/Deft_MKJing/article/details/90348835

orm字段默认类型修改

例如django的orm中对于日期类型,默认精确到小数点后6位,而这些配置基本都在django/db/backends/mysql/base.py下的DatabaseWrapper类当中配置,因此我们可以自己进行修改,举例:

from django.db.backends.mysql.base import DatabaseWrapper
# 修改datetime类型精确到小数点后0位
DatabaseWrapper.data_types["DateTimeField"] = 'datetime(0)'
更多Django相关资料参考

https://www.cnblogs.com/feixuelove1009/p/5823135.html
http://www.cnblogs.com/alex3714/articles/8662706.html

错误信息参考

django.core.exceptions.ImproperlyConfigured: The included URLconf 'invoice.urls' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.

django3+以后如果路由下有代码出现问题都会报这个错,需要自己去排查出现问题的地方,或者可以自动重写代码,捕捉代码中报错的异常,例如在项目配置目录的__init__.py中添加如下代码:

from django.urls.conf import include
from importlib import import_module
from django.core.exceptions import ImproperlyConfigured

def include_rep(arg, namespace=None):
    app_name = None
    if isinstance(arg, tuple):
        # Callable returning a namespace hint.
        try:
            urlconf_module, app_name = arg
        except ValueError:
            if namespace:
                raise ImproperlyConfigured(
                    'Cannot override the namespace for a dynamic module that '
                    'provides a namespace.'
                )
            raise ImproperlyConfigured(
                'Passing a %d-tuple to include() is not supported. Pass a '
                '2-tuple containing the list of patterns and app_name, and '
                'provide the namespace argument to include() instead.' % len(arg)
            )
    else:
        # No namespace hint - use manually provided namespace.
        urlconf_module = arg

    if isinstance(urlconf_module, str):
        try:
            urlconf_module = import_module(urlconf_module)
        except Exception as e:
            # 在此处捕获失败的异常
            print(repr(e), ",问题app:", urlconf_module)
            exit(0)
    patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
    app_name = getattr(urlconf_module, 'app_name', app_name)
    if namespace and not app_name:
        raise ImproperlyConfigured(
            'Specifying a namespace in include() without providing an app_name '
            'is not supported. Set the app_name attribute in the included '
            'module, or pass a 2-tuple containing the list of patterns and '
            'app_name instead.',
        )
    namespace = namespace or app_name
    # Make sure the patterns can be iterated through (without this, some
    # testcases will break).
    if isinstance(patterns, (list, tuple)):
        for url_pattern in patterns:
            pattern = getattr(url_pattern, 'pattern', None)
            if isinstance(pattern, LocalePrefixPattern):
                raise ImproperlyConfigured(
                    'Using i18n_patterns in an included URLconf is not allowed.'
                )
    return (urlconf_module, app_name, namespace)

include.__code__ = include_rep.__code__
# 替换成能够捕获对应异常的函数

Django + uwsgi + nginx部署及一些setting小问题

https://segmentfault.com/a/1190000016108576

定时任务

若希望django服务器启动时,后台能够自动开启某个定时任务,则可参考:
http://www.mamicode.com/info-detail-2305087.html

异步任务

当希望执行某些视图函数时顺带执行某些后台的异步任务时,可以使用celery来完成,参考:
https://blog.csdn.net/Callme_My_Yang/article/details/80882866
https://www.cnblogs.com/cwp-bg/p/8759638.html
http://python.jobbole.com/87238/

Django、Flask、Tornado框架的对比参考

https://www.jianshu.com/p/9960a9667a5c

你可能感兴趣的:(Python Web之Django)