1.Django入门

Django是一个高级Python Web框架,劳伦斯出版集团为了开发以新闻内容为主的网站,而开发出来了这个框架,Django遵守BSD版权,初次发布于2005年7月, 并于2008年9月发布了第一个正式版本1.0 。

1、django特点

1、开发速度快,Django旨在帮助开发人员尽可能快地从概念到应用程序。

2、功能齐全,Django包含了许多可用于处理常见Web开发任务的额外功能,用户身份验证,内容管理,站点地图,RSS源以及许多其他任务。

3、安全性高,Django非常重视安全性,并帮助开发人员避免许多常见的安全错误,例如SQL注入、跨站点脚本、跨站点请求伪造。它的用户身份验证系统提供了一种安全的方法来管理用户账户和密码。

4、Django采用了MVC的软件设计模式,即模型M,视图V和控制器C,并且取了个名字叫MVT。

2、MVC模型的介绍

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

MVC模式最早由Trygve Reenskaug在1978年提出[1] ,是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件设计模式,是为了将传统的输入(input)、处理(processing)、输出(output)任务运用到图形化用户交互模型中而设计的。随着标准输入输入设备的出现,开发人员只需要将精力集中在业务逻辑的分析与实现上。后来JAVA EE采用MVC的设计模式,这种分工开发的方式受到了广大开发者的认可,后来包括java,php,python等语言都有MVC设计模式的框架。

MVC框架的核心思想是解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性。

MVC模式把用户界面交互分拆到不同的三种角色中,使应用程序被分成三个核心部件:Model(模型)View(视图)Control(控制器)

Model: 主要封装对数据库层的访问,内嵌ORM框架,实现面向对象的编程来操作数据库。

View: 用于封装结果,内嵌了模板引擎,实现动态展示数据

Controller: 用于接收用户请求,处理业务逻辑,与Model和View交互,返回结果

在MVC模型中,如果不需要查询数据库,那么Controller将不会与Model交互,直接与View交互,返回html页面。

3、MVT模型介绍

M(Model),与MVC中的M功能相同,负责数据处理,内嵌了ORM框架

V(View),与MVC中的C功能相同,接收请求,业务处理,返回响应

T(Template),与MVC中的V功能相同,负责封装构造要返回的html,内嵌了模板引擎

虚拟环境搭建

在开发中安装模块的方法:

pip install 模块名称

之前我们安装模块都是直接在物理环境下安装,这种安装方法,后面一次安装的会覆盖掉前面一次安装的。那如果一台机器上面开发多个项目使用到不同版本的模块呢?怎么样做才能不受版本影响!那么需要用到虚拟环境,每个虚拟环境互相隔离,在一个虚拟环境中安装卸载模块其他不受影响!

1. python虚拟环境安装

pip install virtualenv
pip install virtualenvwrapper
pip install virtualenvwrapper-win  #Windows使用该命令

上述工具装好后找不到mkvirtualenv命令,需要执行以下环境变量设置。

1.创建目录用来存放虚拟环境

mkdir $HOME/.virtualenvs

2.在~/.bashrc中添加下列两行命令:

export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh

3.运行:

source ~/.bashrc

3.创建python虚拟环境

mkvirtualenv [虚拟环境名称]
workon [虚拟环境名称]

4.退出虚拟环境

deactivate [虚拟环境名称]

5.删除(慎用) 先退回虚拟环境

rmvirtualenv [虚拟环境名称]

6.创建 python 2版本开发

mkvirtualenv -p /usr/bin/python2.7 py2

7.创建 python 3版本开发

mkvirtualenv -p /usr/bin/python3 py3

2.虚拟环境中安装指定版本号的模块

1.在虚拟环境中安装模块不需要加sudo,如果加sudo会安装到真实环境中去,也不需要指定pip的版本,直接使用pip安装就可以。

workon py3 
pip install django==1.8.2

2.查看虚拟环境下安装了那些包

pip freeze list

3.项目开发完成,需要上线时,将开发环境使用的包,导出安装到生产环境下。

pip freeze list > requirements.txt

4.将开发环境导出的包的文件requirements.txt,安装到生产环境

pip install -r requirements.txt

创建项目

1、创建一个博客项目

django的项目结构是由多个应用组成,每一个应用实现不同的功能。

安装好django之后,django提供了一个管理工具 django-admin.py,可以使用 django-admin.py 来创建一个项目。

下面我们来创建一个博客项目,项目名称叫blog。

环境说明:

django 版本1.8.2 这个版本比较稳定,资料比较多,所以本课程使用1.8.2版本

python 3.5.2

unbuntu 16.04

1、在家目录创建一个文件夹保存项目

mkdir myblog
cd myblog

2、进入刚才创建的虚拟环境,创建名称为blog博客项目

workon py3
django-admin startproject blog

执行完命令后在myblog目录下生成一个名问blog项目文件夹。

执行这条命令之后自动创建了一个名为blog项目,并且django帮我们生成了5个文件。 简单介绍下文件:

manage.py:一个命令行工具,可以使你用多种方式对Django项目进行交互

blog/__init__.py:一个空文件,告诉 Python 该目录是一个 Python 包。

blog/setting.py:项目的配置文件

bolg/urls.py:URL分发器(路由配置文件)

blog/wsgi.py:项目与WSGI兼容的Web服务器入口

3、运行项目

python manage.py runserver

看到下面这行提示说明服务已经运行起来

在浏览器中访问:http:127.0.0.1:8000看到下面这个界面说明项目已经正确创建

默认情况下,runserver命令在内部IP的8000端口启动开发服务器。 如果你需改变服务器的端口,把要使用的端口作为一个命令行参数传递给它。 例如,这个命令在8080端口启动服务器:

 python manage.py runserver 8080

如果你需改变服务器的IP地址,把IP地址和端口号放到一起。 因此若要监听所有的外网IP,请使用:

python manage.py runserver 0.0.0.0:8080

2、应用创建

前面提到django项目中一个应用实现一个功能,那刚才创建好了项目,现在我们来创建一个应用。 1、创建一个名称为personal_blog的应用

django-admin startapp personal_blog
或者
python manage.py startapp personal_blog

创建好应用后的整个项目的目录结构:

创建好应用之后的文件介绍:

db.sqlite3 文档型的轻量级数据库,django默认使用sqlite数据库

personal_blog 应用目录名

admin.py web后台管理,用于注册模型类

migrations 保存迁移时生成的文件

models.py 编写模型类,就是MVT模型中的M

tests.py 用于开发测试类

views.py 编写视图函数,MVT模型中的V

3、安装应用

Django 应用是可以“热插拔”的,即可以在多个项目中使用同一个应用,也可以分发这些应用, 因为它们不需要与某个特定的Django安装绑定。

编辑blog/settings.py文件,并修改INSTALLED_APPS设置以包含应用的名称'personal_blog'。

如下代码:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'personal_blog', # 我们自己写的应用
)

前面的这些应用是django自带的应用,如果我们需要新加应用,只需要往后面加。

项目与应用的关系:

应用是一个Web应用程序,它完成具体的事项 —— 比如一个博客系统,一个简单的投票应用。

项目是一个特定网站中相关配置和应用的集合,一个项目可以包含多个应用,一个应用可以运用到多个项目中去。

Admin站点管理

1、站点介绍

在shell下面去编辑博客非常麻烦,前面提到过django自带强大的功能,其中就有一个内容管理。 下面我们来使用django自带的Admin站点管理去编辑我们的blog内容。

  • Django是在新闻编辑室这样的环境中被开发出来的,这样的环境中“内容发布者”站点和“公共”站点有着非常明显的界限,网站管理者使用管理界面来添加新闻故事、新闻事件、体育比赛分数等。这些内容会被展示在公共站点上。Django为网站管理者创建统一的管理界面用以编辑这些内容。
  • Django会根据模型类文件完全自动地生成管理界面。
  • 管理界面不是让访问网站的人使用的,它服务于网站管理者,用于网站的管理员。

2、创建管理员账号

启动开服服务器:

python manage.py runserver

在浏览器中输入 http://127.0.0.1:8000/admin 进入后台管理,但是需要管理员账号密码。默认是没有的,需要开发者创建。

创建后台管理员账号,执行这创建管理员账号命令

python manage.py createsuperuser

执行上面命令后输入按提示输入

Username (leave blank to use 'python'): admin    #用户名
Email address: 123@163.com    # 邮箱
Password: *****              # 密码,注意:在终端上输入密码是看不见的,这里用*号代表密码。实际上是输入你需要设置的密码
Password (again): ****      #重复输入密码
Superuser created successfully.   # 提示successfully 创建成功

用刚才创建的账号密码 登录

3、管理界面本地化

首次登录之后是英文界面,后台管理是给管理员使用的,英文界面阅读起来就比较困难了,这里可以将后台管理本地化。 本地化是将显示的语言、时间等使用本地的习惯,这里的本地化就是进行中国化,中国大陆地区使用简体中文,时区使用亚洲/上海时区,注意这里不使用北京时区表示。

管理界面本地化修改setting.py文件,找到下面两行配置修改

LANGUAGE_CODE 语言 'zh_hans' 表示中文 TIME_ZONE 时区 'Asia/Shanghai' 亚洲/上海

LANGUAGE_CODE = 'zh-hans' #'en-us' 

TIME_ZONE = 'Asia/Shanghai'  #'UTC'

修改好配置之后重启服务:刷新后台管理界面就使用中文显示了

但我们的应用在哪儿? 它没有显示在管理站点的首页面上。

4、注册模型类

如果想要在后台中能编辑的模型类,我们只需要在应用目录下的admin.py中注册模型类:

下面这段代码加入到personal_blog/admin.py中,向admin中注册博客的模型类。

from django.contrib import admin

from personal_blog.models import  *
# Register your models here.

admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(Comment)

重新启动服务刷新后台管理界面,后台管理界面就可以看到所有的模型类了:

点击类名称“Posts”可以进入列表页,默认只有一列,显示的是模型类中 str 方法返回的值

给模型类都添加上str方法

class Category(models.Model):
    # 分类名
    category_name = models.CharField(max_length=20)

    def __str__(self):
        return self.category_name

class Tag(models.Model):
    # 标签名
    tag_name = models.CharField(max_length=20)

    def __str__(self):   
        return self.tag_name

class Post(models.Model):
    ..... #省略代码

    def __str__(self):
        return self.title

class Comment(models.Model):
    ..... #省略代码

    def __str__(self):
        return self.name

添加str方法后,Post列表页显示str返回的title值

进入post列表页可进行操作。

在列表页中点击“增加”可以进入增加页,Django会根据模型类的不同,生成不同的表单控件,按提示填写表单内容后点击"保存",完成数据创建,创建成功后返回列表页

关系字段添加方法

在列表页勾选想要删除的复选框,可以删除多项

点击执行之后会提示,删除内容,询问是否删除,包括关系数据都会一起被删除。 点击确定数据将会被删除,返回则不做删除操作。

自定义管理表单

1、列表页字段展示

只需在 admin.py 中使用admin.site.register(模型类) 注册模型类,Django就能构造一个默认的表单。但是,默认管理表单不够美观,展示的数据量不够,我们需要要自定义管理界面中表单的外观和功能。

  • 在列表页只列出了str方法的返回值,对象的其它属性并没有列出来,查看非常不方便
  • Django提供了自定义管理页面的功能,比如列表页要显示那些字段
  • 打开personal_blog/admin.py文件,自定义类,继承自admin.ModelAdmin类
  • 属性list_display表示要显示哪些属性

自定义Post模型类的管理表单

from django.contrib import admin

from personal_blog.models import  *
# Register your models here.

# admin.site.register(Post) # 将原来注册注释,或删除掉
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(Comment)


# 第一种方法,采用装饰器的方式注册
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title','created_time','modified_time','category','author')  # content内容会很长,不放在列表页展示


# 第二中注册方式
# class PostAdmin(admin.ModelAdmin):
#     list_display = ('title', 'created_time', 'modified_time', 'category', 'author')
#     
# admin.site.register(Post,PostAdmin)

在注册自定义的list_display时,如果有同学将tags添加到list_display中会发现服务器报错了,错误是list_display中的值不能是ManyToManyField类型

django官方解释:ManyToManyField字段不受支持,因为这将需要为表中的每一行执行单独的SQL语句。如果你想这样做,给你的模型一个自定义的方法,并将该方法的名称添加到list_display。

如果我们想要在列表页展示出多对多关系的字段那么需要到模型类中添加一个方法:

class Post(models.Model):
    title = models.CharField(max_length=50)  # 文章标题
    content = models.TextField()  # 文章内容
    created_time = models.DateTimeField()  # 创建时间
    modified_time = models.DateTimeField()  # 发布时间
    category = models.ForeignKey(Category)  # 分类,使用外键连接 一对多关系使用ForeignKey
    tags = models.ManyToManyField(Tag, blank=True)  # 标签,多对多关系使用ManyToManyField
    author = models.ForeignKey(User)  # 作者,一对多关系

    # 添加tags_name方法。在list_display中添加这个方法名
    def tags_name(self):

        return ",".join([str(t) for t in self.tags.all()])   # 这条语句就是将文章的标签取出拼接成字符串返回


    def __str__(self):
        return self.title

写好后将tags_name方法名加到list_display中,这样就不会报错,文章的所有标签页能显示出来。

2、编辑页面排版修改

细心的同学应该注意到了,在增加修改页面的排版问题,django默认是按照模型类编写的循序从上到下展示字段的。但是我们编写模型类字段很多时候并不会去关心字段的顺序。所以在后台编辑的时候不能将相近的字段在一起编辑,给后台编辑带来不便。django在注册模型类的时候提供了fieldsets 属性来控制编辑页面的排版。

修改admin.py文件,PostAdmin类:

fields 属性控制编辑页面展示内容

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title','created_time','modified_time','category','author', 'tags_name')

    fields = ('title','created_time')  # fields 属性控制编辑页面展示那些字段

增加修改页只展示fields属性包含的内容。如果有些字段是默认不需要修改的可以是用fields控制。

显然只用fields并不能提供给我们一个直观的排序方式。要创造一个直观的排序方式就需要fieldsets配合fields使用。

修改admin.py文件中的PostAdmin类:

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title','created_time','modified_time','category','author', 'tags_name')


    fieldsets = (
        ('标题/正文',{'fields':['title','content']}),
        ('创建/修改时间',{'fields':['created_time','modified_time']}),
        ('分类/标签',{'fields':['category','tags']}),
        ('作者',{'fields':['author']})

    )

fieldsets中每个元组的第一个元素是字段集的标题,第二个元素是一个字典fields属性。 设置fieldsets属性后,后台编辑页面字段分类显示!这样对网站编辑比较友好。

隐藏字段集,在字段很多的情况下,编辑页会很长,如果将部分字段集隐藏起来,需要修改的时候再显示,这样编辑起来就方便很多 修改admin.py文件的PostAdmin类:字段集中添加 'classes': ['collapse']

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title','created_time','modified_time','category','author', 'tags_name')


    fieldsets = (
        ('标题/正文',{'fields':['title','content']}),
        ('创建/修改时间',{'fields':['created_time','modified_time'],'classes': ['collapse']}),
        ('分类/标签',{'fields':['category','tags'],'classes': ['collapse']}),
        ('作者',{'fields':['author']})

    )

字段集中添加了'classes': ['collapse']属性的字段集默认将被隐藏起来,需要点显示才能显示出来编辑。

3、添加关联对象

Category跟Post是一对多的关系,编辑Post每个与其它对象拥有ForeignKey关系的对象都有一个这种链接。当你点击“+”,你将看到一个带有“添加 category”表单的弹出窗口。如果你在这个窗口中添加了一个category并点击“Save”,Django会将保存这个category到数据库中,然后动态地将这个对象添加为你正编辑的category的选择项。

django认为这是一个很麻烦的事情,高效的编辑方式应该在创建category对象的时候顺便一起编辑Post。

# admin.site.register(Category)  删除默认注册的 Category,或者注释掉

class PostInline(admin.StackedInline):
    model = Post
    extra = 3

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('category_name',)
    inlines = [ChoiceInline]    # 声明内嵌的模型类

inlines 指定关联的模型类 extra 指定提供几个空白空间,不能被删除。

这告诉Django:Post对象在Category的管理界面中编辑。默认提供足够3个Post的空间。

这里有一个问题,关联表放进来后占用大量的屏幕空间,为了解决这个问题,Django提供了一种以表格的形式显示内嵌的相关联对象的方法,只需改变一下 PostInline 的声明:

使用 TabularInline(不是StackedInline),这些相关联的对象显示成紧凑的、基于表格的形式:

class ChoiceInline(admin.TabularInline):  # 这里继承admin中的 TabularInline类,以表格的形式内嵌关联表对象
    model = Post
    extra = 2  

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('category_name',)
    inlines = [ChoiceInline]

声明为 TabularInline 后这里内嵌的Post就以表格的形式展示

4、列表页表头修改

观察下表头,列名使用的是模型类中的字段名的大写,并且将下划线转成了空格。

如果对列名不满意可以在模型类中定义一个方法代替原来字段,方法返回原来字段的值。将TITLE修改成“标题”。 修改models.py文件Post类:

class Post(models.Model):
    title = models.CharField(max_length=50)  # 文章标题
    content = models.TextField()  # 文章内容
    created_time = models.DateTimeField()  # 创建时间
    modified_time = models.DateTimeField()  # 发布时间
    category = models.ForeignKey(Category)  # 分类,使用外键连接 一对多关系使用ForeignKey
    tags = models.ManyToManyField(Tag, blank=True)  # 标签,多对多关系使用ManyToManyField
    author = models.ForeignKey(User)  # 作者,一对多关系

    def tags_name(self):

        return ",".join([str(t) for t in self.tags.all()])

    def title_name(self):  
        return self.title

    title_name.short_description = '标题'

    def __str__(self):
        return self.title

然后在到admin.py文件中修改PostAdmin类中的list_dispaly元素'title'修改成刚才在models中创建的方法名

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title_name','created_time','modified_time','category','author', 'tags_name')
    ...#省略其他代码

5、添加搜索功能

在admin.py中注册模型类中添加search_fields属性,可以是列表,也可以是元祖。里面的元素是支持搜索的字段名称。

增加标题,分类搜索:在admin.py中PostAdmin类中增加search_fields属性,将需要搜索的字段添加到列表中。

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title_name','created_time','modified_time','category','author', 'tags_name')

    fieldsets = [
        ('标题/正文',{'fields':['title','content']}),
        ('创建/修改时间',{'fields':['created_time','modified_time'],'classes': ['collapse']}),
        ('分类/标签',{'fields':['category','tags'],'classes': ['collapse']}),
        ('作者',{'fields':['author']})
    ]

    search_fields = ['title','category__category_name']  # 添加搜索字段

添加好search_fileds属性后,刷新列表页,会出现搜索框。这样就支持 title,category 值的搜索。


</div>

注意:如果搜索字段里有外键。要注明外键的那个字段,双下划线。否则就会报下面错误。

6、过滤器

再次编辑你的admin.py文件来改进列表页面:使用list_filter来添加过滤器。将下面这行添加进PostAdmin:

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title_name','created_time','modified_time','category','author', 'tags_name')

    fieldsets = [
        ('标题/正文',{'fields':['title','content']}),
        ('创建/修改时间',{'fields':['created_time','modified_time'],'classes': ['collapse']}),
        ('分类/标签',{'fields':['category','tags'],'classes': ['collapse']}),
        ('作者',{'fields':['author']})
    ]

    search_fields = ['title','category']  # 添加搜索字段
    list_filter = ['title','category']   # 过滤器

这行代码添加一个“Filter”侧边栏,管理员可以通过title,category字段对列表内容进行显示过滤:

7、修改表名

在后台管理页面中表名默认使用模型类名称的复数形式。通常情况下,我们都会将表名修改成比较直观的名称。

修改表名使用模型类的一个内置类Meta类。Django模型类的Meta是一个内部类,它用于定义一些Django模型类的行为特性。

Meta选项:

db_table

db_table是指定自定义数据库表明的。Django有一套默认的按照一定规则生成数据模型对应的数据库表明。
Options.db_table
定义该model在数据库中的表名称
  db_table = 'Students'
如果你想使用自定义的表名,可以通过以下该属性
  table_name = 'my_owner_table'

ordering

这个字段是告诉Django模型对象返回的记录结果集是按照哪个字段排序的。
这是一个字符串的元组或列表,没有一个字符串都是一个字段和用一个可选的表明降序的'-'构成。
当字段名前面没有'-'时,将默认使用升序排列。使用'?'将会随机排列

ordering=['created_time'] # 按创建升序排列
ordering=['-created_time'] # 按创建降序排列,-表示降序
ordering=['?created_time'] # 随机排序,?表示随机
ordering=['-created_time','author'] # 以created_time降序,在以author升序排列

verbose_name

给模型类起一个更可读的名字一般定义为中文,名字为复数形式,django自动在后面加一个s
verbose_name = "文章"

verbose_name_plural

指定模型类的名字,这个指定什么就是什么,不会再后面加个s,如果是起中文名,一般使用这个
verbose_name_plural = "文章"

修改modles.py文件模型类,每个模型类创建一个内置Meta类修改模型类名称

class Category(models.Model):
    # 分类名
    category_name = models.CharField(max_length=20)

    def __str__(self):
        return self.category_name
    class Meta:
        verbose_name_plural = '分类'

class Tag(models.Model):
    # 标签名
    tag_name = models.CharField(max_length=20)

    def __str__(self):     # __unicode__ on Python 2
        return self.tag_name

    class Meta:
        verbose_name_plural = '标签'

class Post(models.Model):
    title = models.CharField(max_length=50)  # 文章标题
    content = models.TextField()  # 文章内容
    created_time = models.DateTimeField()  # 创建时间
    modified_time = models.DateTimeField()  # 发布时间
    category = models.ForeignKey(Category)  # 分类,使用外键连接 一对多关系使用ForeignKey
    tags = models.ManyToManyField(Tag, blank=True)  # 标签,多对多关系使用ManyToManyField
    author = models.ForeignKey(User)  # 作者,一对多关系

    class Meta:
        verbose_name_plural = '文章'
        ordering = ['-created_time'] #以创建时间倒序显示结果。

    def tags_name(self):

        return ",".join([str(t) for t in self.tags.all()])

    def title_name(self):
        return self.title

    title_name.short_description = '标题'

    def __str__(self):
        return self.title



class Comment(models.Model):
    name = models.CharField(max_length=20)  # 评论用户
    content = models.TextField()  # 评论内容
    time = models.DateTimeField()  # 发布评论时间
    post = models.ForeignKey(Post)  # 评论文章,一对多关系

    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural = '评论'

8、分页显示

django后台管理页面默认一页显示100条数据,在注册模型类时可以指定一页显示几条数据

修改admin.py文件PostAdmin类:添加list_per_page属性,属性值为一个数字,指定一页显示几条数据

class PostAdmin(admin.ModelAdmin):
    list_display = ('title_name','created_time','modified_time','category','author', 'tags_name')

    fieldsets = [
        ('标题/正文',{'fields':['title','content']}),
        ('创建/修改时间',{'fields':['created_time','modified_time'],'classes': ['collapse']}),
        ('分类/标签',{'fields':['category','tags'],'classes': ['collapse']}),
        ('作者',{'fields':['author']})
    ]
    search_fields = ['title','category__category_name']  # 添加搜索字段
    list_filter = ['title','category']   #
    list_per_page = 10   # 指定一页显示几条数据

9、修改应用的名称

Dajngo在Admin后台默认显示的应用的名称为创建app时的名称。

我们如何修改这个app的名称达到定制的要求呢,其实Django已经在文档里进行了说明。

从Django1.7以后不再使用app_label,修改app相关需要使用AppConfig。我们只需要在应用的__init__.py里面进行修改:

# coding=utf-8
from django.apps import AppConfig
import os

default_app_config = 'personal_blog.BlogConfig'

VERBOSE_APP_NAME = '博客'   # 应用名称


def get_current_app_name(_file):
    return os.path.split(os.path.dirname(_file))[-1]


class BlogConfig(AppConfig):
    name = get_current_app_name(__file__)
    verbose_name = VERBOSE_APP_NAME

七、视图和URL

  • 后台管理页面已经做的差不多了,那下面应该就是做 公共 站点,也就是给别人看的网页部分。
  • 对于django的设计框架MVT,用户在URL中请求的是视图,视图接收请求后进行处理,并将处理的结果返回给请求者

1、原理

视图(view)是Django应用中的一“类”网页,它通常使用一个特定的函数提供服务,并且具有一个特定的模板。例如,在博客应用中,可能有以下视图:

  • 博客首页 —— 显示最新发表的博客。
  • 博客“详细”页面 —— 单篇博客的固定链接页面。
  • 评论 —— 对给定的博客发表评论

在Django中,网页的页面和其他内容都是由视图来传递的(视图对WEB请求进行回应)。每个视图都是由一个简单的Python函数(或者是基于类的视图的方法)表示的。Django通过检查请求的URL(准确地说,是URL里域名之后的那部分)来选择使用哪个视图。

比如访问知乎:https://zhuanlan.zhihu.com/p/32292103 你会发现域名之后会带有类似 /p/32292103 django就是利用url域名之后的这段路径去匹配视图。

Django使用叫做‘URLconf’的配置来为URL匹配视图。 一个URLconf负责使用正则表达式将URL模式匹配到视图。

2、定义视图

  • 视图就是一个Python函数(或者是基于类的视图的方法),被定义在views.py中
  • 视图的第一个参数是HttpRequest类型的对象request,包含了所有请求的信息
  • 视图必须返回HttpResponse对象,包含返回给请求者的响应信息

让我们来编写第一个视图。 打开应用目录下的views.py文件并将以下Python代码写入:

personal_blog/views.py

from django.shortcuts import render

from django.http import HttpResponse
# Create your views here.


def index(request):   # 视图函数默认接收一个  request作为参数,包含了所有请求的信息。
    return HttpResponse('hello world')

这是Django中最简单的视图。 为了能够调用这个视图,我们需要将这个视图映射到URL上 —— 利用一个URLconf。

3、URL配置

项目目录下的settings.py中通过ROOT_URLCONF指定url配置,创建项目的时候会默认指定。

ROOT_URLCONF = 'blog.urls'

默认配置

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),

urlpatterns列表,存储url()对象,这个名称是固定的 urlpatterns中的每个正则表达式在第一次访问它们时被编译 url()对象,被定义在django.conf.urls包中

语法: 1、包含,一般在自定义应用中创建一个urls.py来定义url

url(正则,include('应用.urls'))

2、定义,指定正则和视图的对应关系

url(正则,'视图名称')

正则说明:

1、正则部分推荐使用r,表示字符串不转义,这样在正则表达式中使用\只写一个就可以
2、不能在开始加反斜杠,推荐在结束加反斜杠

错误使用:正则开头加/
r'/index'
r'/index/'

定义一个视图:

def detail(request):
    return HttpResponse('ok')

配置url

from django.conf.urls import url
from personal_blog import views

urlpatterns = [
    url(r'^index/$',views.index),
    url(r'^\d+/',views.detail)
]

URLconf配置规则:

  • 一条URLconf包括url规则、视图两部分
  • url规则使用正则表达式定义
  • 视图就是在views.py中定义的视图

查找视图的过程:在浏览器地址栏中输入url,请求到网站后,获取url信息,然后与编写好的URLconf从上往下逐条匹配,如果匹配成功则调用对应的视图,退出匹配。如果所有的URLconf都没有匹配成功,则返回404错误,

配置url有两种方式,第一种是在项目目录的URLconf配置文件中配置,如下:

blog/urls.py中配置:

from django.conf.urls import include, url
from django.contrib import admin

from personal_blog.views import index    # 将视图导入

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),   # 创建项目时,django自动创建的额访问管理后台的url
    url(r'^index/', index)    # 配置访问index视图函数
]

不推荐使用这种方法,如果一个项目有多个应用,全部url都配置到项目URLconf中,会很混乱,不好维护。

启动服务器,在浏览器中输入http://http:127.0.0.1:8000/index/ ,可以访问到index这个视图了。浏览器接收到hello world 这个返回值。

第二种方法在应用目录中创建一个URLconf,创建一个名为urls.py的文件,创建好后项目文件如下图:

创建好文件之后,需要让项目的URLconf链接到应用的URLconf中。修改项目urls.py:

from django.conf.urls import include, url
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),

    url(r'^blog',include('personal_blog.urls')),  # 符合正则将会进入personal_blog的urls.py中继续匹配。
]

配置好关联之后,回到应用中的URLconf配置中。

personal_blog/urls.py

from django.conf.urls import url
from personal_blog import views

urlpatterns = [
    url(r'^index/$',views.index)
]

配置好后在浏览器输入:http://127.0.0.1:8000/blog/index/ ,同样可以访问到视图函数,以后都使用第二种方法。

4、匹配规则介绍

上面有提到,参与匹配的url只是域名之后。比如:http://127.0.0.1:8000/blog/index/?a=1&b=5 这个url 只会有 blog/index/ 参与匹配,域名以及参数都不参与匹配。

匹配从项目URLconf中开始匹配,项目中的URLconf是那个文件,可以在setting.py的设置文件看到这条配置。URLconf根配置是blog/urls.py这个文件。

ROOT_URLCONF = 'blog.urls'

blog/urls.py文件:

from django.conf.urls import include, url
from django.contrib import admin

from personal_blog.views import index
urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^blog/',include('personal_blog.urls')), # 符合正则将会进入personal_blog的urls.py中继续匹配。
]

blog/index/ 从上到下开始匹配,被 r'^blog/' 这条正则匹配中 ,这条正则是对应包含personal_blog应用中的urls.py的,那么将继续到personal_blog/urls.py中继续匹配 在前面匹配过的内容将不再参与匹配,那么只剩下 index/ 会继续匹配。

personal_blog/urls.py

from django.conf.urls import url
from personal_blog import views

urlpatterns = [
    url(r'^index/$',views.index)
]

继续匹配, index/ 匹配中 r'^index/$' 规则,这条URLconf对应视图,那么将会执行这个视图。 到这里整个匹配过程结束。如果全部正则都没有匹配上,就会报 404 错误请求资源不存在。

5、url中参数提取

django中,可以将url提取值作为参数传给视图。视图函数可以接收

获取值需要在正则表达式中使用小括号,也就是正则的分组。
分为两种方式
   1、位置参数
   2、关键字参数
注意:两种参数的方式不要混合使用,在一个正则表达式中只能使用一种参数方式

位置参数:

定义一个/year/month/day/ 格式的url,需要将year,month,day提取出来传给视图函数

由于日期都是数字格式那么这些编写正则:

url(r'^(\d+)/(\d+)/(\d+)/',views.date)

视图函数接收参数: 正则第一个分组参数将传给视图的第二个形参,第二个传给视图第三个形参,以此类推。

def date(request,year,month,day):

    return HttpResponse('{}-{}-{}'.format(year,month,day))

浏览器访问 http://127.0.0.1:8000/blog/2014/2/12/ ,效果如下

关键参数:

在正则表达式部分为组命名 修改url中的正则表达式。 其中?P部分表示为这个参数定义的名称。

url(r'^(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)/',views.date)

视图函数中的形参,只能用正则中分组的名称。不管关键字参数位置如何传,最终结果是一样的。

def date(request,month,day,year):
    return HttpResponse('{}-{}-{}'.format(year,month,day))

6、URL起别名

一般网页中都有很多链接,其实这些链接就是一个url地址。比如在网页里面使用\首页\</a>.像这样的链接有很多。假如有一天,突然需要改变登录的链接,想将/index 变成/blog/index 这样的话,就需要将url里面的正则改变成^blog/index/$,同时再将<a>标签里面的href变成^blog/index/$。像这样<a>的链接太多了,根本就无法修改。

由于没有学到模板,后面会详细介绍模板,这里知道name作用即可:

这个应用中新建一个 templates 文件夹,在templates中新建一个 index.html index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>


</body>
</html>

创建一个视图,返回index.html

from django.shortcuts import render

from django.http import HttpResponse
# Create your views here.


def index(request):
    return render(request,'index.html')

应用中url配置

url(r'^detail/',views.detail)

在浏览器中访问:http://127.0.0.1:8000/blog/index/

现在需求改了,原来的a标签连接需要修改成 /blog/detail/p/ 那么修改正则后,a标签是不是得全部修改!这样工作量就很大了,如果一个网站很多url,那么该的时候有部分没改过来,那不就出问题了。 那么我们用name参数来改进下:

url增加一个name参数

url(r'^detail/',views.detail,name='detail')

修改模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<br>
{% raw %}
<a href="{% url 'detail'%}">blog</a>
<br>
<a href="{% url 'detail' %}">blog</a>
<br>
<a href="{% url 'detail' %}">blog</a>   # 使用 name参数 {% url 'detail' %} 模板语言
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>
<br>
<a href="/blog/detail/">blog</a>

{% endraw %}
</body>
</html>

修改完后重新访问:http://127.0.0.1:8000/blog/index/ 查看网页源代码:

这种写法,修改url之后,我们看看能不能正常渲染。 修改url:

url(r'^detail/p/',views.detail,name='detail')

使用name参数,在不修改前端页面url的情况下,修改了url,django会在前端页面自动渲染name参数对应的url。其他没有使用name参数的a标签url就没有修改过来。

name参数还有其他用法这里先不细讲,由于刚入门django知识储备还不够,再深入讲解可能大家听不懂,所以先将基础知识学完之后会补充name参数其他用法。

7、视图与模型交互

每个视图函数只负责处理两件事中的一件:返回一个包含所请求页面内容的 HttpResponse对象,或抛出一个诸如Http404异常。 视图可以从数据库中读取记录,或者不读取数据库。下面访问数据库,读取文章标题返回。

修改 index视图

from .models import *    # 导入模型类,.models表示导入当前文件夹下的models

# Create your views here.


def index(request):

    post = Post.objects.all()  # 查询Post表中的所有数据,返回一个列表

    content = ','.join([p.title for p in post])

    return HttpResponse(content)

访问 http://127.0.0.1:8000/blog/index/

模板基础

1、设置模板路径

在上一节中,查询出数据是以字符串形式返回给浏览器的,这显然是不符合网站开发的。要一个好看的界面必然用到css,html,js,如果将这些内容都写成字符串的形式,显然不现实。

  • 为了解决这一问题,django中使用了模板
  • 将前端页面写在模板由视图调用,再返回给浏览器。

在setting.py配置文件中TEMPLATES设置描述了Django将如何加载并渲染模板。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],  
        # 模板路径,定义项目目录下的templates
        # BASE_DIR: 项目路径,在setting中定义  
        # BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

        'APP_DIRS': True,  # 包含应用目录下的 templates目录,设置为True,django也能找到应用目录下的模板。

        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

目录:

2、模板命名空间

我们可以直接将我们的模板放在blog/templates,但实际这样做会很麻烦。Django将选择它找到的名字匹配的第一个模板文件,如果你在不同的应用有相同名字的模板文件,Django将不能区分它们。我们需要将Django指向正确的模板,最简单的方式是使用命名空间。具体实现方式是,将这些模板文件放在以应用的名字来命名的另一个目录下:

3、定义模板

django中的模板,是一个html文件,但是模板由自己的一套语言。

在模板中输出变量语法如下,变量可能是从视图中传递过来的,也可能是在模板中定义的

模板系统使用点号查找语法来访问变量的属性。

{{ 变量名 }}

在模板中编写代码段语法:

{% 代码段 %}

比如:在视图中传一个对象参数给模板。如果模板中需要使用对象的属性那么,使用 对象.属性名 方式访问。

{{ 对象.属性名 }}

编写一个模板:

/blog/templates/personal_blog/title.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
{% raw %}
    {% for p in post_list %}
        <li>{{ p.title }}</li> # 模板系统使用点号查找语法来访问变量的属性。
    {% endfor %}
</ul>
{% endraw %}

</body>
</html>

调用模板分为三步骤:

  • 找到模板
  • 定义上下文
  • 渲染模板

创建一个视图title视图来使用模板:

# 导入模块
from django.http import HttpResponse
from django.template import RequestContext,loader 

def title(request):

    post_list = Post.objects.all()

    # 加载模板文件
    template = loader.get_template('personal_blog/title.html')

    # 定义上下文
    context = RequestContext(request, {
        'post_list': post_list,
    })
    # 渲染模板
    return HttpResponse(template.render(context))

配置url:

url(r'^title/',views.title)

浏览器中访问 http://127.0.0.1:8000/blog/title/ 应该能看到一个形如列表的,文章标题。

4、render快捷渲染函数

django调用模板一般方式是载入一个模板、填充一个context 然后返回一个含有模板渲染结果的HttpResponse对象,django提供了一个快捷函数让调用模板更简单。

使用快捷函数重写视图:

from django.shortcuts import render  #导入render模块
def title(request):

    post_list = Post.objects.all()
    #
    # # 加载模板文件
    # template = loader.get_template('personal_blog/title.html')
    #
    # # 将定义上下文
    # context = RequestContext(request, {
    #     'post_list': post_list,
    # })
    # # 渲染模板

    return render(request,'personal_blog/title.html',context={'post_list':post_list})

render()函数将请求对象作为它的第一个参数,模板的名字作为它的第二个参数,第三个参数向模板中传递的上下文数据。 它返回一个HttpResponse对象,含有用给定的context 渲染后的模板。

博客首页

在模板templates/personal_blog 目录下新建一个index.html 用于做主页展示文章。 为了好看点,加了一些css样式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>BLOG</title>

    <style>
        #navList {
            width: 1200px;
            margin: 0 auto;
            height: 60px;
            background-color: #169fe6;
        }
        #navList li {
            float: left;
            height: 60px;
            line-height: 60px;
            list-style-type: none;
        }
        #navList a {
            padding: 0 20px;
            font-size: 16px;
            display: block;
            cursor: pointer;
            text-decoration: none;
            color: #fff;
            text-shadow: 3px 3px 3px #000;
        }
        #navList a:hover {
            background: #fff;
        }
        .forFlow{
            width: 1200px;
            margin: 25px auto;
        }
        .dayTitle {
        color: #fff;
        background-color: #45bcf9;
        padding: 6px 6px;
        font-size: 12px;
        display: block;
        float: left;
        margin-right: 10px;
        }
        .dayTitle a {
            color: #fff;
            text-decoration: none;
            font-weight: bold;
        }

        .day .postTitle {
            font-size: 21px;
            line-height: 1.5em;
            float: left;
            clear: right;}
        .day .postTitle a {
            text-decoration: none;
                color: #555;
        }

          .day .postTitle a:hover {
                color: #0e90d2;
                text-decoration: none;
        }
        .postCon {
            font-family: 微软雅黑;
            padding: 15px 0;
            clear: both;
        }
        .postDesc {
        clear: both;
        color: #bcbcbc;
        float: none;
        text-align: left;
        line-height: 200%;
        font-size: 12px;
}

        .readmore {
            color: #9ab26b;
            text-decoration: none;

        }


    </style>

</head>


<body>
<ul id="navList">
    <li><a href="{% url 'index' %}">首页</a></li>
    <li><a href="#">联系</a></li>
    <li><a href="#">关于</a>
    <li><a href="/admin/">管理</a></li>
</ul>


{% for p in post_list %}

<div class="forFlow">
    <div class="day">
        <div class="dayTitle">
            <a href="">{{p.created_time}}</a>
        </div>
        <div class="postTitle">
            <a class="" href="">{{p.title}}</a>
        </div>

        <div class="postCon">
            <div class="c_b_p_desc">摘要: &nbsp;&nbsp;&nbsp;{{p.content|slice:'100'}}   {# |slice 过滤器,只取前100个字符  #}

                <a href="" class="readmore">阅读全文</a>
            </div>
        </div>
        <div class="clear"></div>
        <div class="postDesc">posted @ 2018-01-06 19:51 地球守卫者 阅读(32) 评论(0)</div>
    </div>
</div>

{%endfor%}
</body>
</html>

视图函数:

from django.shortcuts import render

from django.http import HttpResponse
from django.template import RequestContext,loader
from .models import *

# Create your views here.


def index(request):
    # 查询所有文章
    post_list = Post.objects.all()

    return render(request,'personal_blog/index.html',context={"post_list":post_list})

配置url:

url(r'^index/$',views.index,name='index'),

重启服务器:浏览 http://127.0.0.1:8000/blog/index/ 效果如下:

2、文章详细页

点击文章标题,和阅读全文应该链接到文章的详细页。

编写templates/personal_blog/detail.html 详细页模块:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ post.title }}</title>
</head>
<body>
<ul>{{ post.title }}</ul>
<li>{{ post.content }}</li>


</body>
</html>

详细页视图,由于详细页需要知道那篇文章。所有要求请求的时候将文章id传到视图。

def detail(request,id):

    # 查询pk为 id 的文章
    post = Post.objects.get(pk=id)

    return render(request, 'personal_blog/detail.html', context={"post": post})

配置详细页url,利用url参数提取的方式获取id。

url(r'detail/(\d+)',views.detail,name='detail')

由于视图需要接收id参数,所以在url中需要将id传给视图,修改首页的标题跟阅读全文的url链接:


{# 使用 url的name参数 动态生成url #}

<a class="" href="{% url 'detail' p.id %}">{{p.title}}</a>

<a href="{% url 'detail' p.id %}" class="readmore">阅读全文</a>

重启服务器 尝试访问详细页:

Iyoyo电子书 一本集作者多年开发经验的python电子书 all right reserved,powered by Gitbook文件修订时间: 2022年 12:15:23