GVKun编程网logo

Django 系列博客(八)(django blog)

8

对于Django系列博客感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解八,并且为您提供关于Anaconda+django写出第一个webapp(八)、Django学习笔记(八)Django

对于Django 系列博客感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解,并且为您提供关于Anaconda+django写出第一个web app(八)、Django 学习笔记(八)Django 的前后台数据传递 页面代码不报错,但是点击按钮后控制台不会输出数据、Django 系列博客(一)、Django 系列博客(七)的宝贵知识。

本文目录一览:

Django 系列博客(八)(django blog)

Django 系列博客(八)(django blog)

<h1 id="django-系列博客八">Django 系列博客(八)
<h2 id="前言">前言

本篇博客介绍 Django 中的模板层,模板都是Django 使用相关函数渲染后传输给前端在显式的,为了想要渲染出我们想要的数据,需要学习模板语法,相关过滤器、标签。

你可能注意到下面的例子返回文本的方式有点特别。当我们使用相关函数来渲染模板的时候,首先是打开了模板文件,然后将需要传入的参数填到文件中在返回给前端。

def current_datetime(request): Now = datetime.datetime.Now() html = 'It is Now %s.' % Now return HttpResponse(html)

尽管这很能直观的解释视图层的工作方式,但直接将 HTML 硬编码到你的视图里却不是个好主意。

  • 对页面设计进行的任何改变都必须对python代码进行相应的更改。站点设计的修改往往比底层python代码的修改要频繁的多,因此如何可以在不进行python代码修改的情况下变更设计,那将会方便的多。
  • python代码编写和html设计是两项不同的工作,大多数专业的网站开发环境都会分配不同的部分或者开发人员来完成。设计者和html/css的编码人员不应该被要求去编辑python的代码来完成他们的工作。
  • 程序员编写python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含python又包含html的文件的编辑工作。

基于上述这些原因,将页面的设计和 Python 的代码分离开来会更干净简洁更易维护。可以使用 Django 的模板系统(Templates System)来实现这种模式。

def current_time(req): import datetime Now = datetime.datetime.Now() html = 'It is Now %s.' % Now
# django模板<a href="https://www.jb51.cc/tag/xiugai/" target="_blank">修改</a>的视图<a href="https://www.jb51.cc/tag/hanshu/" target="_blank">函数</a>
from django.template import Template,Context
<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a> = datetime.datetime.<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>()
t = '<html><body>It is <a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a> <a href="https://www.jb51.cc/tag/s/" target="_blank">%s</a>.</body></html>' % <a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>
c = Context({'current_date': str(<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>)})
html = t.render(c)
return HttpResponse(html)

# 另一种写法
import datetime
<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a> = datetime.datetime.<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>()
return render(req,'current_datetime.html',{'current_date': str(<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>)[:19]})</code></pre>

模板语法终点:

  1. 变量:{{ 变量名 }}
    1. 深度查询,用点语法
    2. 过滤器
  2. 标签:{{ % % }}

在 Django 模板中遍历复杂数据结构的关键是点语法

def template_test(request): name = 'lqz' li = ['lqz',1,'18'] dic = {'name': 'lqz','age': 18} ll2 = [ {'name': 'lqz','age': 18},{'name': 'lqz2','age': 19},{'name': 'egon','age': 20},{'name': 'kevin','age': 23} ] ll3=[] class Person: def __init__(self,name): self.name = name
    def test(self):
        print('test<a href="https://www.jb51.cc/tag/hanshu/" target="_blank">函数</a>')
        return 11

    @classmethod
    def test_classmethod(cls):
        print('类<a href="https://www.jb51.cc/tag/fangfa/" target="_blank">方法</a>')
        return '类<a href="https://www.jb51.cc/tag/fangfa/" target="_blank">方法</a>'

    @stati<a href="https://www.jb51.cc/tag/cme/" target="_blank">cme</a>thod
    def static_method():
        print('静态<a href="https://www.jb51.cc/tag/fangfa/" target="_blank">方法</a>')
        return '静态<a href="https://www.jb51.cc/tag/fangfa/" target="_blank">方法</a>'

lqz = Person('lqz')
egon = Person('egon')
person_list = [lqz,egon]
bo = True
te = <a href="https://www.jb51.cc/tag/test/" target="_blank">test()</a>
import datetime
<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>=datetime.datetime.<a href="https://www.jb51.cc/tag/Now/" target="_blank">Now</a>()
link1='<a href="https://www.baidu.com"&gt;点我<a>'
from django.utils import safestring
link=safestring.mark_safe(link1)
# html特殊符号对照表(http://tool.chinaz.com/Tools/htmlchar.aspx)

# 这样传到前台不会变成特殊字符,因为django给处理了
dot='&amp;spades;'


# return render(request,'template_index.html',{'name':name,'person_list':person_list})
return render(request,locals())</code></pre>

<h3 id="html文件">html文件


<pre>

{{ name }}

{{ li }}

{{ dic }}

{{ lqz }}

{{ person_list }}

{{ bo }}

{{ te }}

        <h3>深度查询句点符</h3>
        <p>{{ li.1 }}</p>
        <p>{{ dic.name }}</p>
        <p>{{ lqz.test }}</p>
        <p>{{ lqz.name }}</p>
        <p>{{ person_list.0 }}</p>
        <p>{{ person_list.1.name }}</p>


        <h3>过滤器</h3>
        {#注意:冒号后面不能加空格#}
        <p>{{ now | date:"Y-m-d H:i:s" }}</p>

        {#如果变量为空,设置默认值,空数据,None,变量不存在,都适用#}
        <p>{{ name |default:'数据为空' }}</p>
        {#计算长度,只有一个参数#}
        <p>{{ person_list |length }}</p>
        {#计算文件大小#}
        <p>{{ 1024 |filesizeformat }}</p>

        {#字符串切片,前闭后开,前面取到,后面取不到#}
        <p>{{ 'hello world lqz' |slice:"2:-1" }}</p>
        <p>{{ 'hello world lqz' |slice:"2:5" }}</p>

        {#截断字符,至少三个起步,因为会有三个省略号(传负数,1,2,3都是三个省略号)#}
        <p>{{ '刘清政 world lqz' |truncatechars:"4" }}</p>
        {#截断文字,以空格做区分,这个不算省略号#}
        <p>{{ '刘清政   是      大帅比 谢谢' |truncatewords:"1" }}</p>

        <p>{{ link1 }}</p>
        <p>{{ link1|safe }}</p>
        <p>{{ link }}</p>

        <p>&amp;spades;</p>
        <p>{{ dot }}</p>

        {#add   可以加负数,传数字字符串都可以#}
        <p>{{ "10"|add:"-2" }}</p>
        <p>{{ li.1|add:"-2" }}</p>
        <p>{{ li.1|add:2 }}</p>
        <p>{{ li.1|add:"2" }}</p>
        <p>{{ li.1|add:"-2e" }}</p>

        {#upper#}
        <p>{{ name|upper }}</p>
        <p>{{ 'LQZ'|lower }}</p>

        <h3>模版语法之标签</h3>
        {#for 循环 循环列表,循环字典,循环列表对象#}
        <ui>
            {% for foo in dic %}
                {{ foo }}
            {% endfor %}
            {#也可以混用html标签#}
            {% for foo in li %}
                <ul>foo</ul>

            {% endfor %}
        </ui>
        {#表格#}
        <table border="1"&gt;

            {% for foo in ll2 %}
                <tr&gt;
                    <td&gt;{{ foo.name }}</td&gt;
                    <td&gt;{{ foo.age }}</td&gt;
                </tr&gt;
            {% endfor %}
        </table&gt;
        <table border="1"&gt;
            {#'parentloop': {},'counter0': 0,'counter': 1,'revcounter': 4,'revcounter0': 3,'first': True,'last': False}#}
            {% for foo in ll2 %}
                <tr&gt;
                    <td&gt;{{ forloop.counter }}</td&gt;
                    <td&gt;{{ foo.name }}</td&gt;
                    <td&gt;{{ foo.age }}</td&gt;
                </tr&gt;
            {% endfor %}


        </table&gt;



        {% for foo in ll5 %}
            <p>foo.name</p>
        {% empty %}
            <p>空的</p>
        {% endfor %}


        <h3>if判断</h3>
        {% if name %}
            <a href=""&gt;hi {{ name }}</a>
            <a href=""&gt;注销</a>
        {% else %}
            <a href=""&gt;请登录</a>
            <a href=""&gt;注册</a>
        {% endif %}
        {#还有elif#}

        <h3>with</h3>
        {% with ll2.0.name as n %}
            {{ n }}
        {% endwith %}
        {{ n }}


        {% load my_tag_filter %}

        {{ 3|multi_filter:3 }}

        {#传参必须用空格区分#}
        {% multi_tag 3 9 10 %}

        {#可以跟if连用#}
        {% if 3|multi_filter:3 > 9 %}
            <p>大于</p>
        {% else %}
            <p>小于</p>
        {% endif %}</code></pre>

注意:点语法也可以用来引用对象的方法(无参数方法)

字典:{{ dic.name.upper }}

语法:

{{obj|filter__name:param}}  变量名字|过滤器名称:变量

如果一个变量是 false 或者为空,使用给定的默认值。否则使用变量的值。

{{ value|default:"nothing" }}

返回值得长度。它对字符串和列表都有效。

{{ value|length }}

如果value['a','b','c','d'],那么输出是4。

将值格式化为一个‘’人类可读的文件尺寸‘’('13KB','4.1MB'等)。

{{ value|filesizeformat }}

如果 value 是 123456789,输出将会是 117.7 MB。 

输出格式化时间

{{ value|date:"Y-m-d" }}

如果value=datetime.datetime.now(),那么结果会是2019-01-10

该方法只能传入一个参数,通常使用引号包裹。

{{ file_size|filesizeformat }}

如果后台的 file_size为1024,那么结果会是1KB

如果字符串字符多于指定的字符数量,那么会被阶段。截断的字符串将以可翻译的省略号序列('...')结束。

参数:要截断的字符数

{{ value|truncatechars:9 }}

Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。比如:

value="" # 不希望转义的链接
{{ value|safe}} # 添加 safe 过滤器就可以不转义了

过滤器 描述 示例 输出加上一个数值加上转义号irst一个字母大写irst,返回 good输出指定长度的字符串,把变量居中删除指定字符串默认值代替默认值代替一个dictionaryzeformat增加数字的可读性,转换结果为13KB,89MB,3Bytes等zeformat 返回1.0KBirst一个列表默认保留1位小数截取指定位置的数字或标签包裹变量

Hi

David

标签代替换行符加上行号输出指定长度的字符串,变量左对齐字符串转换为列表pluralize输出英文复数符号随机一项删除字符串中指定的HTML标记输出指定长度的字符串,变量右对齐删除,空格用减号替换返回日期的时间部分显示时间变量显示时间变量runcatewords字符串转换为省略表达方式runcatewords:2 返回'This is ...'runcatewords_html标签This is a pen

'|truncatewords:2,返回

This is ...

链接
标签">模板之标签

标签看起来像是这样的:{% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模板中。一些标签需要开始和结束标签(例如{% tag %}...标签 内容... {% endtag %})。

标签

遍历每一个元素:

{% for person in person_list %}
    

{{ person.name }}

{% endfor %}

可以利用&#123;% for obj in list reversed %&#125;反向完成循环。

遍历一个字典:

{% for key,val in dic.items %}
    

{{ key }}:{{ val }}

{% endfor %}

注:循环序号可以通过``{{ forloop }}显示

forloop.counter            The current iteration of the loop (1-indexed) 当前循环的索引值(从1开始)
forloop.counter0           The current iteration of the loop (0-indexed) 当前循环的索引值(从0开始)
forloop.revcounter         The number of iterations from the end of the loop (1-indexed) 当前循环的倒序索引值(从1开始)
forloop.revcounter0        The number of iterations from the end of the loop (0-indexed) 当前循环的倒序索引值(从0开始)
forloop.first              True if this is the first time through the loop 当前循环是不是第一次循环(布尔值)
forloop.last               True if this is the last time through the loop 当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环

for 标签带有一个可选的&#123;% empty %&#125; 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。

{% for person in person_list %}
    

{{ person.name }}

{% empty %}

sorry,no person here

{% endfor %}

标签

&#123;% if %&#125;会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if num > 100 or num < 0 %}
    

无效

{% elif num > 80 and num < 100 %}

优秀

{% else %}

凑活吧

{% endif %}

if语句支持:and、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的

例如:

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}
不要写成as

{% csrf_token%}

这个标签用于跨站请求伪造保护

标签和过滤器">自定义标签和过滤器

  1. 在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.

  2. 在app中创建templatetags模块(模块名只能是templatetags)

  3. 创建任意 .py 文件,如:my_tags.py

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 = "" %(id,arg,)
return mark_safe(result)

  1. 使用自定义 simple_tag和 fileter 的 html 文件中导入之前创建的 my_tags.py
{% load my_tags %}
  1. 使用simple_tag和filter(如何调用)
{% 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 %}

注意:filter 可以用在 if 等语句后,simple_tag不可以

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

Anaconda+django写出第一个web app(八)

Anaconda+django写出第一个web app(八)

今天来实现网站的登入和登出功能。

首先我们需要在urls.py中添加路径,注意此处的路径和在导航栏中设置的文字路径保持一致:

from django.urls import path
from . import views

app_name = ''main'' #此处为了urls的命名空间

urlpatterns = [
    path('''', views.homepage, name=''homepage''),
    path(''register/'', views.register, name=''register''),
    path(''logout/'', views.logout_request, name=''logout_request''),
    path(''login/'', views.login_request, name=''login_request''),
]

在view.py中添加登出和登入的函数,登出的函数非常简单,只需要使用logout函数登出并给出登出成功的信息,然后导向主页即可。登入函数需要使用新的表单AuthenticationForm,并判断用户是否存在,如果存在则登入并进入主页,如果不存在则给出错误信息:

from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import Tutorial
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import login, logout, authenticate
from django.contrib import messages
# Create your views here.
def homepage(request):
    return render(request=request, 
            template_name=''main/home.html'',
            context={''tutorials'':Tutorial.objects.all})

def register(request):
    if request.method == ''POST'':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            username = form.cleaned_data.get(''username'')
            messages.success(request, f"New account created: {username}")
            login(request, user)
            return redirect(''main:homepage'')
        else:
            for msg in form.error_messages:
                messages.error(request, f"{msg}:{form.error_messages[msg]}")

            return render(request=request,
                template_name=''main/register.html'',
                context={''form'':form})

    form = UserCreationForm
    return render(request=request,
            template_name=''main/register.html'',
            context={''form'':form})

def logout_request(request):
    logout(request)
    messages.info(request, f"Logged out successfully!")
    return redirect(''main:homepage'')

def login_request(request):
    if request.method == ''POST'':
        form = AuthenticationForm(request=request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get(''username'')
            password = form.cleaned_data.get(''password'')
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                messages.info(request, f"You are now logged in as {username}")
                return redirect(''main:homepage'')
            else:
                messages.error(request, f"Invalid username or passowrd.")
        else:
            messages.error(request, f"Invalid username or passowrd.")


    form = AuthenticationForm
    return render(request=request,
            template_name=''main/login.html'',
            context={''form'':form})

登入登出及信息显示如下,第二幅图显示“You are now logged in as xiaohonghong”,这个就是toast的效果:

 

 

 

与之前的几节不同,我在这里修改了导航栏的颜色,并添加了container效果,修改方法如下,通过style设置背景颜色,将container加入到div的class中:

<nav style="background-color: #008E72">
    <div class="nav-wrapper container">

现在使用的注册表单是materialize默认的,我们希望注册的时候可以加入邮箱这个选项,这样忘记密码的时候就可以通过邮箱来找回密码,如何自定义注册表单呢?方法如下,在main文件夹下新建forms.py:

from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User

class NewUserForm(UserCreationForm):
    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ("username", "email", "password1", "password2")

    def save(self, commit=True):
        user = super(NewUserForm, self).save(commit=False)
        user.email = self.cleaned_data.get(''email'')
        if commit:
            user.save()
        return user

然后将views.py中的UserCreationForm修改为NewUserForm即可,在浏览器点击Register得到如下界面,可以看到email这一样出现了:

参考链接:

[1] https://pythonprogramming.net/user-login-logout-django-tutorial/?completed=/messages-django-tutorial/

 

Django 学习笔记(八)Django 的前后台数据传递 页面代码不报错,但是点击按钮后控制台不会输出数据

Django 学习笔记(八)Django 的前后台数据传递 页面代码不报错,但是点击按钮后控制台不会输出数据

@AzureMonkey 你好,想跟你请教个问题:使用ajxa无法在控制台输出数据,还有vue.js如果如是空文件html页面会在new Vue({}处提示不是一个正确的方法。以及网上下载的axios.min.js文件是否跟您用的一样。

Django 系列博客(一)

Django 系列博客(一)

Django 系列博客(一)

前言

学习了 python 这么久,终于到了Django 框架。这可以说是 python 名气最大的web 框架了,那么从今天开始会开始从 Django框架的安装到使用一步步的学习,这系列博客不会像前端的那样水了(立个 flag),希望可以成为高质量的博客。那么本篇博客介绍 Django 的安装以及如何在电脑上运行第一个 Django 应用。

Django 的安装

Django 的安装很简单,在 win 和 mac 上面都可以使用 pip 安装命令安装,也可以通过 pycharm 安装,或者下载文件在命令行使用安装工具安装。

接下来我在 ubuntu 上安装做示例。

在这里还安装了一个依赖包 pytz。这是用来做时区转换的一个第三方库。

其他平台的 pip 安装方式一样,不过要选用 python3的对应 pip 来安装,因为现在的 Django 版本已经不支持 python2了。

虚拟环境的安装

什么是虚拟环境

  • 对真实的 python 解释器的一个拷贝版本
  • 事实有效,可以独立存在并运行解释 python 代码
  • 可以在计算机上拷贝多个虚拟环境

为什么要使用虚拟环境

  • 保证真实环境的纯净性
  • 框架的多版本共存
  • 方便做框架的版本迭代
  • 降低多框架共存的维护成本

安装虚拟环境

  1. 通过 pip 安装虚拟环境库

因为我之前已经下载好了,所以这里直接显示请求已经完成,并且后面是安装的绝对路径。

  1. 前往目标文件夹

这个文件夹是你用来保存虚拟环境的文件夹,该文件夹一旦确定就不要轻易更改。

image-20190103154126721

这个 py3-env1是创建的一个纯净虚拟环境。

  1. 创建纯净的虚拟环境
virtualenv 虚拟环境名 (py3-env2)
  1. 终端启动虚拟环境
cd py3-env1\Scripts
activate
  1. 进入虚拟环境下的 python 开发环境
python3
  1. 关闭虚拟环境
deactivate
  1. Pycharm的开发配置
添加:创建项目 -> Project Interpreter -> Existing interpreter -> Virtualenv Environment | System Interpreter -> 目标路径下的 python.exe
删除:Setting -> Project -> Project Interpreter -> Show All

mac 电脑从第三步直接到最后一步就好

了解

# 创建非纯净环境:
#	-- virtualenv-clone 本地环境 虚拟环境名
# Mac配置终端,在终端运行虚拟环境
# 在用户根目录下的.bash_profile(没有需手动创建)文件中设置
# alias 终端指令名(env-py3)=''/Library/Virtualenv/虚拟环境名/bin/python3''
# alias 终端指令名(env-pip3)=''/Library/Virtualenv/虚拟环境名/bin/pip3''

HTTP 协议

因为 Django 框架应用层是采用的 HTTP 协议,所以有必要了解 HTTP 协议。

什么是 HTTP 协议

  • HTTP(HyperText Transport Protocol) 是超文本传输协议,而 HTTPS 也归属于 HTTP 协议,S 代表安全。
  • 基于 TCP/IP协议基础上的应用层协议,底层实现仍为 socket
  • 基于请求-响应模式:通信一定是从客户端开始,服务端接收到客户端一定会做出对应响应
  • 无状态:协议不对任何一次通信状态和任何数据做保存
  • 无连接:一次连接只完成一次请求-响应,请求-响应完毕后会立即断开连接。

HTTP 工作原理

一次 HTTP 连接称之为一个事务,过程可以分为四步

  1. 客户端与服务端建立连接
  2. 客户端发生一个 HTTP 协议指定格式的请求
  3. 服务端接收请求后,回应一个 HTTP 协议指定格式的响应
  4. 客户端将服务端的响应展现给用户

HTTP 状态码

  • 1开头:

  • 2开头:

  • 3开头:

  • 4开头:

  • 5开头:

请求报文

# 请求行  请求头  请求体
''''''
POST / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n
usr=abc&pwd=123
''''''

响应报文

# 响应行  响应头  响应体
''''''
HTTP/1.1 200 OK\r\n
Content-type:text/html\r\n
\r\n
Login Success
''''''

使用原生 socket 完成和浏览器的交互

目录结构

01_socket
	-- 01_client.html:前台通过form表单访问后台的页面
	-- 01_login.html:后台测试渲染给前台的登录页面
	-- 01_index.html:后台测试渲染给前台的主页
	-- 01_server.py:后台服务器文件

因为 B/S架构的客户端已经由浏览器写好,所以只需要关注服务器端就ok。

服务器端代码

from socket import socket

# 设置响应头(包含响应行)
RESP_HEADER = b''HTTP/1.1 200 OK\r\nContent-type:text/html;charset=utf-8\r\n\r\n'' # 连续两个\r\n表示响应头结束

# 设置服务器 socket 相关信息
server = socket()
server.bind('''', 8080) # 空字符串表示绑定本机
server.listen(5)
print((''服务:http://localhost:8080''))

while True:
    # 获取以 http 协议发来的请求
    client, addr = server.accept()
    data = client.recv(1024)
    # 数据报文 包含请求行 请求头 请求体
    print(data)
    client.send(RESP_HEADER)
    
    # /index => 响应主页
    # /login => 登录页面
    # 错误 => 404
    # 数据 data, 字节形式 => 字符串形式
    strData = str(data, encodeing)
    
    # 解析请求的数据,分析得到路由
    my_route = strData.split(''\r\n'')[0].split('' '')[1]
    
    # 后台没有设置的路由,统统由404来处理
    dt = b''404''
    
    # 设置的路由返回响应的页面文件
    if my_route == ''/index'':
        with open(''index 页面路径'', ''rb'') as f:
            dt = f.read()
    if my_route == ''/login'':
        with open(''login 页面路径'', ''rb'') as f:
            dt = f.read()
            
    # /favicon.ico该请求是往后台请求标签图标
    if my_route == ''/favicon.ico'':
        with open(''favicon.ico'', ''rb'') as f:
            dt = f.read()
    # 服务器发送响应体
    client.send(dt)
    # 一次循环,代表一次响应,也就是一次事务的完成,要关闭 http 请求连接
    client.close()

修改返回数据,完善响应体

# 字符串
client.send(b''HTTP/1.1 200 OK\r\n'')
client.send(b''\r\n'')
client.send(b''Normal Socket Web'')
# html代码,请求头要设置支持 html 代码
client.send(b''HTTP/1.1 200 OK\r\n'')
client.send(b''Content-type:text/html\r\n'')
client.send(b''\r\n'')
client.send(b''<h1>Normal Socket Web</h1>'')
# html文件(同级目录建立一个index.html页面)
client.send(b''HTTP/1.1 200 OK\r\n'')
client.send(b''Content-type:text/html\r\n'')
client.send(b''\r\n'')
# 利用文件方式读取页面
with open(''01_index.html'', ''rb'') as f:
    dt = f.read()
client.send(dt)

拓展

修改接收数据,模拟后台路由

# 分析接收到的数据
data = client.recv(1024)
# 保证接收到的数据作为字符串进行以下处理
data = str(data, encoding=''utf-8'')
# 拆分出地址位
route = data.split(''\r\n'')[0].split('' '')[1]
# 匹配地址,做出不同的响应
if route == ''/index'':
    with open(''01_index.html'', ''rb'') as f:
    	dt = f.read()
elif route == ''/login'':
    with open(''01_login.html'', ''rb'') as f:
    	dt = f.read()
else:
    dt = b''404''
client.send(dt)

框架演变

目录结构

02_frame
	-- favicon.ico
	-- index.html
	-- manage.py

manage.py

import socket
import pymysql
# 响应头
RESP_HEADER = b''HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n''

# 请求处理
def index():
    # 以字节方式读取文件
    with open(''index.html'', ''rb'') as f:
        dt = f.read()
    return dt
def ico():
    with open(''favicon.ico'', ''rb'') as f:
        dt = f.read()
    return dt
def user():
    # 数据库操作
    conn = pymysql.connect(host=''127.0.0.1'', port=3306, db=''django'', user=''root'', password=''root'')
    cur = conn.cursor(pymysql.cursors.DictCursor)
    cur.execute(''select * from user'')
    users = cur.fetchall()
    print(users)
    users = ''''''%d:%s
    %d:%s'''''' % (users[0][''id''], users[0][''name''], users[1][''id''], users[1][''name''])
    return users.encode(''utf-8'')

# 设置路由
urls = {
    # 请求路径与请求处理函数一一对应
    ''/index'': index,
    ''/favicon.ico'': ico,
    ''/user'': user
}

# 设置socket
def serve(host, port):
    server = socket.socket()
    server.bind((host, port))
    print(''start:http://'' + host + '':'' + str(port))
    server.listen(5)
    while True:
        sock, addr = server.accept()
        data = sock.recv(1024)
        data = str(data, encoding=''utf-8'')
        print(data)
        route = data.split(''\r\n'')[0].split('' '')[1]

        resp = b''404''
        if route in urls:
            resp = urls[route]()

        sock.send(RESP_HEADER)
        sock.send(resp)
        sock.close()

# 启服务
if __name__ == ''__main__'':
    serve(''127.0.0.1'', 8002)

项目演变

目录结构

03_proj
	-- template
		-- index.html
		-- user.html
	favicon.ico
	start.py
	urls.py
	views.py

index.html

<h1>{{ name }}</h1>

user.html

<table border="1">
    <tr>
        <th>id</th>
        <th>name</th>
        <th>password</th>
    </tr>
    {% for user in users%}
    <tr>
        <td>{{user.id}}</td>
        <td>{{user.name}}</td>
        <td>{{user.password}}</td>
    </tr>
    {% endfor %}
</table>

start.py

from wsgiref.simple_server import make_server
from urls import urls


def app(env, response):
    print(env)
    # 设置响应头
    response("200 OK", [(''Content-type'', ''text/html'')])
    route = env[''PATH_INFO'']
    print(route)
    data = urls[''error'']()
    if route in urls:
        data = urls[route]()
    # 返回二进制响应体
    return [data]


if __name__ == ''__main__'':
    server = make_server(''127.0.0.1'', 8003, app)
    print(''start:http://127.0.0.1:8003'')
    server.serve_forever()

urls.py

from views import *
urls = {
    ''/index'': index,
    ''/favicon.ico'': ico,
    ''/user'': user,
    ''error'': error
}

views.py

import pymysql
# 利用jinja2来渲染模板,将后台数据传给前台
from jinja2 import Template

def index():
    with open(''templates/index.html'', ''r'') as f:
        dt = f.read()
    tem = Template(dt)
    resp = tem.render(name=''主页'')
    return resp.encode(''utf-8'')

def ico():
    with open(''favicon.ico'', ''rb'') as f:
        dt = f.read()
    return dt

def user():
    # 数据库操作
    conn = pymysql.connect(host=''127.0.0.1'', port=3306, db=''django'', user=''root'', password=''root'')
    cur = conn.cursor(pymysql.cursors.DictCursor)
    cur.execute(''select * from user'')
    users = cur.fetchall()
    print(users)

    with open(''templates/user.html'', ''r'') as f:
        dt = f.read()
    tem = Template(dt)
    resp = tem.render(users=users)

    return resp.encode(''utf-8'')

def error():
    return b''404''

Django 系列博客(七)

Django 系列博客(七)

<h1 id="django-系列博客七">Django 系列博客(七)
<h2 id="前言">前言

本篇博客介绍 Django 中的视图层中的相关参数,HttpRequest 对象、HttpResponse 对象、JsonResponse,以及视图层的两种响应方式 CBV 和 FBV,还有简单的文件上传。

函数

一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。

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

from django.shortcuts import render,HttpResponse,HttpResponseRedirect,redirect import datetime

def current_datetime(request):
Now = datetime.datetime.Now()
html = "It is Now %s." % Now
return HttpResponse(html)

这段代码解析:

  • django.shortcuts模块导入了HttpResponse类,以及Python的datetime库;
  • 定义了current_datetime函数。它就是视图函数。每个视图函数都使用HttpRequest对象作为第一个参数,并且通常称之为request

注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。

  • 会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse对象。

在视图层最重要的就是要熟悉两个对象:请求对象(request)和响应对象(HttpResponse)。

属性

Django 将请求报文中的请求行、请求头、请求体封装成 HttpRequest 类中的属性。除了特殊说明之外,其他的均为只读属性。

1.HttpRequest.GET

  一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。

2.HttpRequest.POST

  一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。

  POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。
因此,不应该使用 if request.POST 来检查使用的是否是POST 方法;应该使用 if request.method == "POST"
  另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。

注意:键值对的值是多个的时候,比如checkBox类型的input标签,select标签,需要用:
request.POST.getlist("hobby")

3.HttpRequest.body

  一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。
  但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST 。

4.HttpRequest.path

  一个字符串,表示请求的路径组件(不含域名)。
  例如:"/music/bands/the_beatles/"

5.HttpRequest.method

  一个字符串,表示请求使用的HTTP 方法。必须使用大写。
  例如:"GET"、"POST"

6.HttpRequest.encoding

  一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8')。
这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。
接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。

7.HttpRequest.Meta

  一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
  取值:

CONTENT_LENGTH —— 请求的正文的长度(是<a href="https://www.jb51.cc/tag/yige/" target="_blank">一个</a>字符串)。
CONTENT_TYPE —— 请求的正文的MIME 类型。
HTTP_ACCEPT —— 响应可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
HTTP_HOST —— 客服端发送的HTTP Host 头部。
HTTP_REFERER —— Referring <a href="https://www.jb51.cc/tag/yemian/" target="_blank">页面</a>。
HTTP_USER_AGENT —— 客户端的user-agent 字符串。
QUERY_STRING —— 单个字符串形式的<a href="https://www.jb51.cc/tag/chaxun/" target="_blank">查询</a>字符串(未解析过的形式)。
REMOTE_ADDR —— 客户端的IP 地址。
REMOTE_HOST —— 客户端的主机名。
REMOTE_USER —— 服务器认证后的<a href="https://www.jb51.cc/tag/yonghu/" target="_blank">用户</a>。
REQUEST_METHOD —— <a href="https://www.jb51.cc/tag/yige/" target="_blank">一个</a>字符串,例如"GET" 或"POST"。
SERVER_NAME —— 服务器的主机名。
SERVER_PORT —— 服务器的端口(是<a href="https://www.jb51.cc/tag/yige/" target="_blank">一个</a>字符串)。

  从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 Meta 的键时,
都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_ 前缀。
所以,一个叫做 X-Bender 的头部将转换成 Meta 中的 HTTP_X_BENDER 键。

8.HttpRequest.FILES

  一个类似于字典的对象,包含所有的上传文件信息。
FILES 中的每个键为 中的name,值则为对应的数据。
  注意,FILES 只有在请求的方法为POST 且提交的

带有enctype="multipart/form-data" 的情况下才会
包含数据。否则,FILES 将为一个空的类似于字典的对象。

9.HttpRequest.COOKIES

  一个标准的Python 字典,包含所有的cookie。键和值都为字符串。

10.HttpRequest.session

  一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
完整的细节参见会话的文档。

11.HttpRequest.user(用户认证组件下使用)

  一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。

  如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。

例如:

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.


   user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。

 -------------------------------------------------------------------------------------

<a href="https://www.jb51.cc/tag/nimingyonghu/" target="_blank">匿名用户</a>
class models.AnonymousUser

django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:

id 永远为None。
username 永远为空字符串。
get_username() 永远返回空字符串。
is_staff 和 is_superuser 永远为False。
is_active 永远为 False。
groups 和 user_permissions 永远为空。
is_anonymous() 返回True 而不是False。
is_authenticated() 返回False 而不是True。
set_password()、check_password()、save() 和delete() 引发 NotImplementedError。
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。</code></pre>

<h3 id="request常用方法">request常用方法
<pre>1.HttpRequest.get_full_path()

  返回 path,如果可以将加上查询字符串。

  例如:"/music/bands/the_beatles/?print=true"
  注意和path的区别:http://127.0.0.1:8001/order/?name=lqz&amp;age=10

2.HttpRequest.is_ajax()

  如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串'XMLHttpRequest'。

  大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。

  如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware,
你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应能够正确地缓存。


<h2 id="httpresponse-对象">HttpResponse 对象

响应对象主要有三种形式:前面的博客有相关介绍

  1. HttpResponse();
  2. render();
  3. redirect().

HttpResponse()括号内直接跟一个具体的字符串作为响应体,比较直接和简单,所以这里介绍后两种

render(request,template_name[,context])

结合给定的模板和一个上下文字典,返回一个经过 Django渲染后的HttpResponse 对象到前端。

参数:

  1. request:用于生成响应的请求对象;
  2. template_name:要使用的模板的完整名称,可选的参数;
  3. context:添加到模板上下文的一个字典。默认是一个空字典,如果字典中的某个值是可调用的,视图将在渲染模板之前调用它;

render 方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个 HTML 页面作为响应返回给前端。

传递要重定向的一个硬编码的URL

def my_view(request): ... return redirect('/some/url/')

也可以是一个完成的 url

def my_view(request): ... return redirect('http://www.baidu.com/') 

向前端返回一个 json 格式的字符串,有两种方式:

import json data = {'name': 'musibii','age': 18} return HttpResponse(json.dumps(data)) # 从这里可以看出其实 Jsonresponse内部也是调用了 json 的 dumps 方法进行转换

from django.http import JsonResponse data = {'name': 'musibii','age': 18}

return JsonResponse(data1,safe=False)

safe参数是一种安全机制,因为如果要传输列表类型的数据时,会因为内部的相关机制会产生错误。

<h2 id="cbv-和-fbv">CBV 和 FBV

CBV 基于类的视图(Class Base View)和 FBV 基于函数的视图(Function Base View)

from django.views import View

class Login(View):

def <a href="https://www.jb51.cc/tag/dis/" target="_blank">dis</a>patch(self,request,*args,**<a href="https://www.jb51.cc/tag/kwargs/" target="_blank">kwargs</a>):
    print(request)
    print(args)
    print(<a href="https://www.jb51.cc/tag/kwargs/" target="_blank">kwargs</a>)
    # 可以写类似装饰器的东西,在前后加<a href="https://www.jb51.cc/tag/daima/" target="_blank">代码</a>
    obj=<a href="https://www.jb51.cc/tag/super/" target="_blank">super()</a>.<a href="https://www.jb51.cc/tag/dis/" target="_blank">dis</a>patch(request,**<a href="https://www.jb51.cc/tag/kwargs/" target="_blank">kwargs</a>)
    return obj

def get(self,request):
    return render(request,'login.html')

def post(self,request):
    name = request.POST.get('name')
    ret = BookInfo.objects.filter(name=name).f<a href="https://www.jb51.cc/tag/irs/" target="_blank">irs</a>t()
    print(type(ret))
    dic = {}
    print(ret.name)
    print(type(ret.__dict__))
    for key,value in (ret.__dict__).items():
        if key[0].startswith('_'):
            continue
        print(key)
        dic[key] = value
        # print(dic)

   # 第二种
   # ret = BookInfo.objects.all() 
   # lis = []
   # for info in ret:
   #     dic = {}
   #     for k,v in (info.__dict__).items():
   #         if key[0].startswitch(''):
   #             continue
   #         dic[key] = v
   #     lis.append(dic)

   # 第三种
   # li = []
   # ret = BookInfo.objects.all()
   # for book in ret:
   #     dic = {}
   #     for field in book._<a href="https://www.jb51.cc/tag/Meta/" target="_blank">Meta</a>.fields:
   #         dic[field.name] = getattr(book,field.name)
   #     li.append(dic)
   return JsonResponse(dic,json_dumps_p<a href="https://www.jb51.cc/tag/ara/" target="_blank">ara</a>ms={'ensure_ascii': False})</code></pre>

<h3 id="fbv">FBV


<pre>def file_upload(request):
if request.method=="GET":
return render(request,'file_upload.html')
else:
print(request.POST)
print(request.FILES)

print(request.body)

    # FILES是<a href="https://www.jb51.cc/tag/yige/" target="_blank">一个</a>字典,# <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 1.jpg (image/jpeg)>]}>
    # 拿到<a href="https://www.jb51.cc/tag/shangchuan/" target="_blank">上传</a>的<a href="https://www.jb51.cc/tag/wenjian/" target="_blank">文件</a>对象

    file=request.FILES.get('myfile')
    print(type(file))
    from django.core.files.uploadedfile import InMemoryUploadedFile
    with open(file.name,'wb') as f:
        # for line in file.chunks():
        for line in file:
            f.write(line)
    return HttpResponse('<a href="https://www.jb51.cc/tag/shangchuan/" target="_blank">上传</a>成功')</code></pre>

<h2 id="简单文件上传">简单文件上传
<h3 id="模板文件">模板文件
<pre># upload_file.html
<!DOCTYPE html>
<html lang="en">

<Meta charset="UTF-8"> Title

<form action="" method="post" enctype="multipart/form-data">

用户名:

文件

from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^upload_file/$',views.UploadFile.as_view()),]

文件

class UploadFile(View):
def get(self,'upload_file.html')

def post(self,request):
    file = request.FILES.get('myfile')
    # print(file['file'])
    from django.core.files.uploadedfile import InMemoryUploadedFile
    print(time.time())
    filename = str(time.time()).split('.')[0] + file.name
    with open(filename,'wb') as f:
        for line in file:
            f.write(line)
    return HttpResponse('<a href="https://www.jb51.cc/tag/shangchuan/" target="_blank">上传</a>成功')</code></pre>

今天关于Django 系列博客的介绍到此结束,谢谢您的阅读,有关Anaconda+django写出第一个web app(八)、Django 学习笔记(八)Django 的前后台数据传递 页面代码不报错,但是点击按钮后控制台不会输出数据、Django 系列博客(一)、Django 系列博客(七)等更多相关知识的信息可以在本站进行查询。

本文标签: