web框架Django

Posted 鵬程萬裏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web框架Django相关的知识,希望对你有一定的参考价值。

一、什么是web框架?

  框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单的说,就是你用别人搭建好的舞台来做表演。

  对于所有的web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。

  最简单的web应用就是先把html用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

  如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

  正确的做法是底层代码由专门的服务器软件实现,我们用python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用python编写web业务。这个接口就是WSGI:Web Server Gateway Interface。

  

from wsgiref.simple_server import make_server

def foo1(request):
    f=open("alex.html","rb")
    data = f.read()
    f.close()
    return [data]

def foo2(request):
    f = open("amos.html", "rb")
    data = f.read()
    f.close()
    return [data]

def login(request):
    f = open("login.html", "rb")
    data = f.read()
    f.close()
    return [data]

def reg(request):
    f = open("reg.html", "rb")
    data = f.read()
    f.close()
    return [data]

def auth(request):
    print("+++++",request)

    user_union,pwd_union=request.get("QUERY_STRING").split("&")
    _,user=user_union.split("=")
    _,pwd=pwd_union.split("=")

    if user ==\'amos\' and pwd == \'123\':
        return ["登陆成功".encode()]
    else:
        return [b\'user or pwd is wrong\']

def routers():

    URLpattern = (
        (\'/login\',login),
        (\'/auth\',auth),
        (\'/alex\',foo1),
        (\'/amos\',foo2),
        (\'/reg\',reg),
    )

    return  URLpattern


def application(environ,start_response):
    # environ里打包了所有的请求信息
    path=environ.get("PATH_INFO")
    print(path)
    start_response(\'200 OK\',[(\'Content-Type\',\'text/html;charset=utf8\')]) #返回头信息,可以写多个键值对

    urlpattern=routers()
    func=None

    for item in urlpattern:
        if path==item[0]:
            func=item[1]
            break

    if func:
        return func(environ)
    else:
        return [b\'404\']
    # if path=="/amos":
    #     f=open("amos.html","rb")
    #     data=f.read()
    #     f.close()
    #     return [data]
    # elif path==\'/alex\':
    #     f = open("alex.html", "rb")
    #     data = f.read()
    #     f.close()
    #     return [data]
    # else:
    #     return [b\'<h1>404</h1>\']


t=make_server("",8080,application) #实例化对象

t.serve_forever()
自己编写简单框架
整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,
我们只负责在更高层次上考虑如何响应请求就可以了。

application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。

Python内置了一个WSGI服务器,这个模块叫wsgiref    
    
    
application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

        //environ:一个包含所有HTTP请求信息的dict对象;
        
        //start_response:一个发送HTTP响应的函数。

在application()函数中,调用:

start_response(\'200 OK\', [(\'Content-Type\', \'text/html\')])

就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每
个Header用一个包含两个str的tuple表示。

通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。

然后,函数的返回值b\'<h1>Hello, web!</h1>\'将作为HTTP响应的Body发送给浏览器。

有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,
通过start_response()发送Header,最后返回Body。

注意
注意事项

二、MVC和MTV模式  

  Django的MTV模式本质是各组件之间为了保持松耦合关系,Django的MTV分别代表:

  Model(模型):负责业务对象与数据库的对象(ORM)

  Template(模板):负责如何把页面展示给用户

  View(视图):负责业务逻辑,并在适当的时候调用Model和Template

  此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template

  

Django基本命令

1、创建一个Django project

django-admin startproject project_name

 

  当前目录下会生成mysite的工程,目录结构如下:

  

  • manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
  • settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
  • urls.py ----- 负责把URL模式映射到应用程序。

2、在mysite目录下创建应用,比如blog:

python manage.py startapp blog

  

3、启动Django项目

python manage.py runserver 8080

二、路由配置系统(URLconf)

  URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

  功能:客户端访问的url的路径(path)与视图函数一一映射关系

  语法格式:

urlpatterns = [
        url(正则表达式, views视图函数,参数,别名),
]

参数说明:

    一个正则表达式字符串
    一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
    可选的要传递给视图函数的默认参数(字典形式)
    别名:一个可选的name参数,可以让前端都用别名,这样代码维护起来就比较方便,比如
    <form action="{% url \'别名\' %}" method="post">
     这样不管你的后端路径怎么改,都不会影响到前端代码

2.1:URLconf的正则字符串参数

  2.1.1:简单配置

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r\'^articles/2003/$\', views.special_case_2003),
    url(r\'^articles/([0-9]{4})/$\', views.year_archive),
    url(r\'^articles/([0-9]{4})/([0-9]{2})/$\', views.month_archive),
    url(r\'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$\', views.article_detail),
\'\'\'
    NOTE:
一旦匹配成功则不再继续
若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
每个正则表达式前面的\'r\' 是可选的但是建议加上。

一些请求的例子:

    /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
    /articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。
    /articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数
                       views.month_archive(request, \'2005\', \'03\')。
    \'\'\'

 2.1.2:有名分组(named group)

  上面的实例使用简单的、没有命令的正则表达式组(通过括号)来捕获URL中的值并以位置参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL中的值并以关键字参数传递给视图。

  在python正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。

  下面是以上URLconf使用命名组打的重写:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r\'^articles/2003/$\', views.special_case_2003),
    url(r\'^articles/(?P<year>[0-9]{4})/$\', views.year_archive),
    url(r\'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$\', views.month_archive),
    url(r\'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$\', views.article_detail),
]

这个实现与前面的实例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。例如:

/articles/2005/03/    
请求将调用views.month_archive(request, year=\'2005\', month=\'03\')函数
/articles/2003/03/03/ 
请求将调用函数views.article_detail(request, year=\'2003\', month=\'03\', day=\'03\')。

 在实际应用中,这意味你的URLconf会更加清晰且不容易产生参数顺序问题的错误——你可以在你的视图函数定义中重新安排参数的顺序。当然,这些好处是以简洁为代价;有些开发人员认为命名分组语法丑陋而繁琐。

  2.1.3:URLconf在什么 上查找

  URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。

  例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/

  在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/

  URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POSTGETHEAD等等 —— 都将路由到相同的函数。

  2.1.4:捕获的参数永远是字符串

  每个捕获的参数都作为一个普通的Python字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf中:

url(r\'^articles/(?P<year>[0-9]{4})/$\', views.year_archive),

views.year_archive() 的year 参数将是一个字符串

  2.1.5:指定视图参数的默认值

  有一个方便地小技巧是指定视图参数的默认值。下面是一个URLconf和视图的示例:

# URLconf
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r\'^blog/$\', views.page),
    url(r\'^blog/page(?P<num>[0-9]+)/$\', views.page),
]

# View (in blog/views.py)
def page(request, num="1"):

    ...

在上面的例子中,两个URL模式指向同一个视图views.page —— 但是第一个模式不会从URL 中捕获任何值。如果第一个模式匹配,page() 函数将使用num参数的默认值"1"。如果第二个模式匹配,page() 将使用正则表达式捕获的num 值。

2.2:name参数

\'\'\'

urlpatterns = [
    url(r\'^index\',views.index,name=\'INDEX\'),

]
###################

def index(req):
    if req.method==\'POST\':
        username=req.POST.get(\'username\')
        password=req.POST.get(\'password\')
        if username==\'alex\' and password==\'123\':
            return HttpResponse("登陆成功")

    return render(req,\'index.html\')

#####################

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#     <form action="/index/" method="post">#}
     <form action="{% url \'INDEX\' %}" method="post">
         用户名:<input type="text" name="username">
         密码:<input type="password" name="password">
         <input type="submit" value="submit">
     </form>
</body>
</html>


#######################

\'\'\'

三、编写视图

  一个视图函数,或者剪短来说叫做视图,是一个简单的python函数,它接收web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片等等,是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它再你的python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了能够把代码放在某个地方,惯例是把视图放在叫做views.py的文件中,饭后把它放到你的项目或者应用目录里。

3.1:一个简单的视图

  下面是一个返回当前日期和时间作为HTML文档的视图:

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

让我们逐行阅读上面的代码:

  • 首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。
  • 接着,我们定义了current_datetime函数。它是一个视图函数。每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。
  • 注意视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。
  • 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都要返回HttpResponse对象

  http请求-响应过程中有两个核心对象:

  http请求对象:HttpRequest

  http响应响应:HttpResponse

3.2:快捷函数

  3.2.1:render函数

---------------render(request, template_name[, context])

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

参数:
     request: 用于生成响应的请求对象。

     template_name:要使用的模板的完整名称,可选的参数

     context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

     content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。

     status:响应的状态码。默认为200。

3.2.2:redirect(跳转)函数

-----------------------------------url.py

 url(r"login",   views.login),
 url(r"amos_back",   views.amos_back),

-----------------------------------views.py
def login(req):
    if req.method=="POST":
        if 1:
            # return redirect("/amos_back/")
            name="amos"

            return render(req,"my backend.html",locals())

    return render(req,"login.html",locals())


def amos_back(req):

    name="Amos"

    return render(req,"my backend.html",locals())

-----------------------------------login.html

<form action="/login/" method="post">
    <p>姓名<input type="text" name="username"></p>
    <p>性别<input type="text" name="sex"></p>
    <p>邮箱<input type="text" name="email"></p>
    <p><input type="submit" value="submit"></p>
</form>
-----------------------------------my backend.html
<h1>用户{{ name }}你好</h1>

#总结: render和redirect的区别:
#redirect走的是路径,render返回的是模板

#   1 if render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分
#     除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦.

#   2 the most important: url没有跳转到/amos_back/,而是还在/login/,所以当刷新后
#     又得重新登录.

四、Template

4.1:模板支持的语法

模板语言:渲染变量{{变量名}}       渲染标签{% %}

目的:将变量嵌入到html中
  注意:
     1.只要带有模板语法的HTML都称为模板
     2.render方法渲染时  把后端变量嵌入到模板中
 
 locals():可以替换kye,value类型的上下文对象,他会把后端所有的变量嵌入到前端页面中,前
  端直接可以用

4.2:深度查询(万能的句点号)

#最好是用几个例子来说明一下。
# 首先,句点可用于访问列表索引,例如:

>>> from django.template import Template, Context
>>> t = Template(\'Item 2 is {{ items.2 }}.\')
>>> c = Context({\'items\': [\'apples\', \'bananas\', \'carrots\']})
>>> t.render(c)
\'Item 2 is carrots.\'

#假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点:
>>> from django.template import Template, Context
>>> person = {\'name\': \'Sally\', \'age\': \'43\'}
>>> t = Template(\'{{ person.name }} is {{ person.age }} years old.\')
>>> c = Context({\'person\': person})
>>> t.render(c)
\'Sally is 43 years old.\'

#同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有
#year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
>>> d.month
>>> d.day
>>> t = Template(\'The month is {{ date.month }} and the year is {{ date.year }}.\')
>>> c = Context({\'date\': d})
>>> t.render(c)
\'The month is 5 and the year is 1993.\'

# 这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适
# 用于任意的对象。
>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template(\'Hello, {{ person.first_name }} {{ person.last_name }}.\')
>>> c = Context({\'person\': Person(\'John\', \'Smith\')})
>>> t.render(c)
\'Hello, John Smith.\'

# 点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可以使用同样的句点语法来调用它们:
>>> from django.template import Template, Context
>>> t = Template(\'{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}\')
>>> t.render(Context({\'var\': \'hello\'}))
\'hello -- HELLO -- False\'
>>> t.render(Context({\'var\': \'123\'}))
\'123 -- 123 -- True\'

# 注意这里调用方法时并* 没有* 使用圆括号 而且也无法给该方法传递参数;你只能调用不需参数的
# 方法。

4.3:变量的过滤器(filter)使用

语法格式:      {{obj|filter:param}}

truncatewords:3 按单词截断
truncatechars:20 按字符截断

如果从后端传一个标签,需要通过一个过滤器safe告诉前端这是一个安全的标签
 add          :  给变量加上相应的值
 addslashes  :    给变量中的引号前加上斜线
 capfirst    :    首字母大写
 cut          :  从字符串中移除指定的字符
 date        :  格式化日期字符串
 default      :  如果值是False,就替换成设置的默认值,否则就是用本来的值
 default_if_none:  如果值是None,就替换成设置的默认值,否则就使用本来的值

4.4:标签的使用(使用大括号百分比的组合来表示使用tag)

  4.4.1:{% if %} 的使用

{% if num >= 100 and 8 %}

    {% if num > 200 %}
        <p>num大于200</p>
    {% else %}
        <p>num大于100小于200</p>
    {% endif %}

{% elif num < 100%}
    <p>num小于100</p>

{% else %}
    <p>num等于100</p>

{% endif %}

{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量
{% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的:

{% if obj1 and obj2 or obj3 %}

4.4.2:{% for %} 的使用

<ul>
{% for obj in list %}
    <li>{{ obj.name }}</li>
{% endfor %}
</ul>


#在标签里添加reversed来反序循环列表:

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

#{% for %}标签可以嵌套:

    {% for country in countries %}
        <h1>{{ country.name }}</h1>
        <ul>
         {% for city in country.city_list %}
            <li>{{ city }}</li>
         {% endfor %}
        </ul>
    {% endfor %}


#系统不支持中断循环,系统也不支持continue语句,{% for %}标签内置了一个forloop模板变量,
#这个变量含有一些属性可以提供给你一些关于循环的信息

1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1:

    {% for item in todo_list %}
        <p>{{ forloop.counter }}: {{ item }}</p>
    {% endfor %}
2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0
3,forloop.revcounter
4,forloop.revcounter0
5,forloop.first当第一次循环时值为True,在特别情况下很有用:

    
    {% for object in objects %}   
         {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}   
         {{ object }}   
        </li>  
    {% endfor %}  
    
# 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了
# 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它
# Django会在for标签的块中覆盖你定义的forloop变量的值
# 在其他非循环的地方,你的forloop变量仍然可用


#{% empty %}   如果循环对象为空时显示里面内容

{{li }}
      {%  for i in li %}
          <li>{{ forloop.counter0 }}----{{ i }}</li>
      {% empty %}
          <li>this is empty!</li>
      {% endfor %}

#         [11, 22, 33, 44, 55]
#            0----11
#            1----22
#            2----33
#            3----44
#            4----55
View Code

 

4.4.3:{% url %}

#引用路由配置的地址

<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>

4.4.4:{% with %}

#用更简单的变量名替代复杂的变量名

{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}

4.4.5:{% verbatim %}

#禁止render

{% verbatim %}
         {{ hello }}
{% endverbatim %}

 4.4.6:自定义filter和simple_tag

  区别:
       filter:只可以接收一个参数,可以用于if等控制语句
       simple_tag:可以接收多个参数,但不可以用于if等控制语句
1.查看app是否在配置文件中
2.在app中创建templatetags模块(必须的)
3.创建任意 .py 文件,如:my_tags.py
4.在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py :{% load my_tags %}

定义方法

from django import template
from django.utils.safestring import mark_safe

register = template.Library()  #register的名字是固定的,不可改变

@register.filter
def filter_multi(v1,v2):
    return  v1 * v2

@register.simple_tag
def simple_tag_multi(v1,v2):
    return  v1 * v2

@register.simple_tag
def my_input(id,arg):
    result = "<input type=\'text\' id=\'%s\' class=\'%s\' />" %(id,arg,)
    return mark_safe(result)

使用方法

{% load xxx %}  #首行
   
# num=12
{{ num|filter_multi:2 }} #24            自定义过滤器
{{ num|filter_multi:"[22,333,4444]" }}

#自定义标签
{% simple_tag_multi 2 5 %}  参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}

4.5:extend母版继承

  作用:减少公用页面区域的重复代码

#在母版中想要替换的地方写这条内容

{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

子页面引用时

{% extends "base.html" %}
 
{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

工作方式:

  在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由{% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。

       注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到

以上是关于web框架Django的主要内容,如果未能解决你的问题,请参考以下文章

Django Web框架入门

web框架底层原理;django介绍

web框架本质及Django的安装

web框架之--Django基础入门

Django框架开发-web框架

Linux(CentOS7)系统中部署Django web框架