Django入门 — 建立简易博客以及ORM关系介绍

2018/4/20 posted in  Python
  • 开源的Python Web框架
  • MVC模式

使用Django 必须先安装Python,推荐安装3.0版本以上的Python3
然后再用pip安装工具安装Django
由于我安装的是python3 所以我的pip工具是pip3
安装命令pip3 install django==2.0
最后选择适合自己的IDE
我使用的是VSCode

优点

  • 开发效率高
  • 功能强大,丰富的第三方插件
  • 重视安全,避免安全漏洞

命令语句django-admin 检查django是否成功安装。

屏幕快照 2019-05-20 上午11.19.16

  • startproject # 创建一个django项目
  • startapp # 创建一个django应用
  • check # 检验项目完整性
  • runserver # 本地简易运行django项目
  • shell # 进入django项目的python shell环境
  • test # 执行django用例测试

数据库相关

  • makemigrations # 创建模型变更的迁移文件
  • migrate # 执行上一个命令创建的迁移文件
  • dumpdata # 把数据库数据导出到文件
  • loaddata # 把文件数据导入到数据库

Django 应用 VS Django项目

首先要明白Django项目和应用的区别

  • 1个Django项目就是一个基于Django的web应用
  • 1个Django应用就是一个可重用的python软件包
  • 1个Django应用可以自己管理模型视图模版路由和静态文件
  • 1个Django项目可以包含一组配置和若干个Django应用。

创建一个Django项目

  1. 创建一个项目

    django-admin startproject django_intrduction
    
  2. 用VSCode打开项目文件夹

    结构目录如下

    Django_instrduction

    • Django_instrduction

      • init.py//项目初始化
      • setting.py//🌟项目配置文件
      • urls.py//项目路由配置文件
      • wsgi.py//python服务器网关接口
    • manage.py//项目管理文件

  3. 运行项目 终端命令python3 manage.py runserver

Django项目重要文件介绍

setting.py
import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#项目根目录


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '7bet#*gq%**^7*)5m%)avx&sg6yv_^&t5b!@8+$(&of&_^1wv)'#项目安全码

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True #不要在正式环境中打开 如果打开的话异常会抛到前端

ALLOWED_HOSTS = []
#ALLOWED_HOSTS = ['localhost'] 只允许host是数组中的内容的地址访问我们的网站 其他地址都屏蔽了


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog.apps.BlogConfig',#自己创建的应用要写在这里面 才能被识别
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'django_intrduction.urls'

#django中的模板就是一个个的html文件
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        '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',
            ],
        },
    },
]


创建一个Django应用

  1. 创建一个应用

    运行项目 终端命令python3 manage.py startapp 应用名

  2. 用VSCode打开项目文件夹

    结构目录如下

    Django_instrduction

    • 应用名

      • views.py//🌟执行响应的代码所在模块,大部分代码都在这里
      • models.py//定义应用模型、使用orm框架
      • apps.py //声明应用的地方
      • admin.py //定义admin模块管理的地方、后台管理配置
      • tests.py //自动化测试模块,编写测试用例的地方
      • urls.py //(自行创建)管理应用路由的地方
    • Django_instrduction

      • init.py//项目初始化
      • setting.py//项目配置文件
      • urls.py//项目路由配置文件
      • wsgi.py
    • manage.py//项目管理文件

Django HelloWorld

Django 视图

大部分代码都写在views.py当中,所以编辑views是非常重要的,

  • 每个响应对应一个函数,函数必须返回一个响应。
  • 函数必须存在一个参数,一般约定为request
  • 每个响应函数对应一个url
//views.py
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def hello_world(request):
    return HttpResponse("hello world")

Django 路由

  • 每个URL都以url的形式写出来
  • url函数放在urlpatterns列表中
应用路由
from django.urls import path,include

import blog.views


urlpatterns = [
    path("hello_world",blog.views.hello_world)
]
项目路由
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include("blog.urls")),
]

概念介绍及实战

Django 模型

模型层简介
模型层是什么
  • 位于视图层和数据库之间
  • python对象和数据表之间转换
为什么需要模型层
  • 屏蔽不同的数据库之间的差异
  • 开发者更加专注于业务逻辑的开发
  • 提供很多便捷工具有助开发
创建博客文章模型
设计播客文章模型
  • 文章标题——文本类型
  • 文章摘要——文本类型
  • 文章内容——文本类型
  • 唯一id标记——int类型(自增、主键)
  • 发布日期——日期类型
模型层常用字段
  • 数字类型:
    • IntegerField()#整数 11个字节
    • BigIntegerField()#整数 20个字节
    • PositiveIntegerField()#正整数 10个字节
    • SmallIntegerField()#整数 6个字节
    • PositiveSmallIntegerField()#正整数 5个字节
  • 文本类型:
    • TextField()#longtext
    • CharField()#varchar
    • 布尔类型
    • BooleanField()#允许为空
    • NullBooleanField()#不允许为空
  • 浮点型
    • FloatField()
    • DecimalField()#指定整数和小数各多少位
  • 日期类型:
    • DateTimeField()#年月日时分秒
    • DateField()# 年月日
    • DurationField()#一段时间 int类型
  • 自增ID:
    • AutoField()
    • BigAutoField()//接受更大的值
  • 主键定义:primary_key属性
  • 二进制数据

    • BinaryField()
  • 其他类型

    • EmailField()#邮箱
    • ImageField()#图片
    • FileField()#文件
    • FilePathField()#文件路径
    • UrlField()
    • UUIDField()
    • GenericIPAddressField()#ip地址
模型层关系型字段
  • 一对一(OneToOneField)
  • 多对一(ForeignKey)
  • 多对多(ManyToManyField)
生成数据表
  1. 命令行进入到manage.py同级目录
  2. 执行python3 manage.py makemigrations appname(可选)
  3. 执行python3 manage.py migrate
Demo
  1. 在应用的 models.py 定义模型
from django.db import models
# Create your models here.
class Aritcle(models.Model):
    
    aritcle_id = models.AutoField(primary_key=True)
    title = models.TextField()
    brief_content = models.TextField()
    content = models.TextField()
    publish_date = models.DateTimeField(auto_now=True)
    
    def __str__(self):
        return self.title

# 演示关系型model      
class A(models.Model):
    onetoone = models.OneToOneField(Aritcle)

class B(models.Model):
    foreign = models.ForeignKey(A)

class C(models.Model):
    manytomany = models.ManyToManyField(B)
       
  1. 然后迁移模型定义到数据库当中

    • python3 manage.py makemigrations appname
    Alexs-MacBook-Air:django_intrduction alexwee$ python3 manage.py makemigrations blog
    Migrations for 'blog':
    blog/migrations/0001_initial.py
    - Create model Aritcle
  2. 运行迁移文件 把迁移文件的内容同步到数据库中

    • python3 manage.py migrate appname
    Alexs-MacBook-Air:django_intrduction alexwee$ python3 manage.py migrate  blog
    Operations to perform:
    Apply all migrations: blog
    Running migrations:
    Applying blog.0001_initial... OK
Django shell

Python shell 用于交互式的python编程
Django shell 也类似继承django项目环境

为什么要使用django shell
  • 临时性操作使用django shell更加方便
  • 小范围的debug更简单,不需要运行整个项目来测试
  • 方便开发 方便调试 方便debug
demo

使用django shell 新建文章

  1. python3 manage.py shell进入shell 环境

    Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
    [Clang 6.0 (clang-600.0.57)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>>
  2. 在shell环境新建文章并保存到数据库

    from blog.models import Aritcle
    >>> a = Aritcle()
    >>> a.title = "test django shell"
    >>> a.brief_content = "test django shell by maweefeng"
    >>> a.content = "test django shell new ariticle main content"
    >>>
    >>>
    >>>
    >>> print(a)
    Aritcle object (None)
    >>> a.save()//保存到数据库
    >>>
  3. 验证文章是否保存到数据库

    >>> ariticle = Aritcle.objects.all()//取出所有文章
    >>> aricle = ariticle[0]//取到第一篇文章
    >>> print(aricle.title)
    <class 'django.db.models.fields.TextField'>
    >>>
    >>> print(aricle)
    Aritcle object (1)
    >>> print(aricle.content)
    <class 'django.db.models.fields.TextField'>
    >>>
django admin模块
django admin模块是什么
  • 是一个后台管理工具,不需要用户开发后台管理工具
  • 可以读取定义的模型元数据,提供强大的管理使用页面
为什么要使用django admin模块
  • django shell 新建文章太复杂了
  • 管理页面是基础设施中重要的部分
  • 认证用户,显示管理模型,校验输入等功能类似
使用方法
  • 创建管理员用户 python3 manage.py createsuperuser
* Alexs-MacBook-Air:django_intrduction alexwee$ python3 manage.py createsuperuser
Username (leave blank to use 'alexwee'): maweefeng
address: 
Password: 
Password (again): 
Superuser created successfully.
Alexs-MacBook-Air:django_intrduction alexwee$ 

但是没有看到刚才新建的文章模型,是因为我们还没有把模型注册到admin中,进入IDE中的应用目录下的admin.py

from django.contrib import admin

# Register your models here.

from .models import Aritcle

admin.site.register(Aritcle)  

回到浏览器刷新 就有了新的分组

屏幕快照 2019-05-21 上午11.20.06

实现播客数据返回页面

类似于之前返回的helloworld项目

  1. 应用视图 views.py

    from django.shortcuts import render
    from django.http import HttpResponse
    from blog.models import Summary
    # Create your views here.
    def hello_world(request):
    return HttpResponse("hello world")
    def ariticle_content(request):
    summary = Summary.objects.all()[0]
    title = summary.summary_title
    content = summary.summary_content
    summary_id = summary.summary_id
    pubdate = summary.publish_date
    return_str = 'title:%s summary_content:%s summary_id:%s publicdate:%s'%(title,content,summary_id,pubdate)
    return HttpResponse(return_str)
  2. 应用路由 urls.py

    from django.urls import path,include
    import blog.views
    urlpatterns = [
    path("hello_world",blog.views.hello_world),
    path("content",blog.views.ariticle_content)
    ]
  3. 项目路由 urls.py

    from django.contrib import admin
    from django.urls import path,include
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include("blog.urls")),
    ]

Django视图与模板

使用Bootstrap实现静态博客页面

页面布局设计

  • 博客首页
  • 文章详情页

Bootstrap 以及 Bootstrap的栅格系统

Bootstrap是来自美国twitter的前端框架,提供非常多的控件以及源码,栅格系统就是把web页面分成几等份排列的方式。

创建模板文件夹并实现静态页面

在应用文件夹内部新建文件夹templates,然后在里面新建html文件,这是很关键的一步

⚠️注意事项

还有需要注意的是最好在templates文件夹中再新建应用名所属的文件夹,然后在应用名所属的文件夹中新建html文件,这是因为Django查找templates是按照INSTALLED_APPS中的添加顺序查找templats,所以不同应用下的templates的html同名会造成冲突。

认识Django的模版系统

python的视图文件不适合编码html,因为页面设计改变需要修改python代码,比较复杂。另外网页的逻辑和视图应该分开设计。

模版系统的表现形式是文本,分离文档的表现形式和表现内容,模版系统定义了特有的标签占位符。

学习模版系统的基本语法

  1. 变量标签:{{变量}}
  2. for循环标签:{% for x in list %},{% endfor %}

      <ul>
        {% for item in list%}
    <li>{{item}}</li>
    {% endfor%}
    </ul>
  3. if-else标签:{% if %},{% else %},{%endif%}

     {% if true %}
            <p>it is true part</p>
    {%else%}
    <p>it is false part</p>
    {% endif %}

使用模版系统渲染博客页面

render渲染函数

  • render() 函数中支持一个dict类型的参数
  • 该字典是后台传递到模板的参数,键为参数名
  • 在模板中使用{{参数名}}来直接使用

    def render(request, template_name, context=None, content_type=None, status=None, using=None)
    Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments.
    

编写django应用的views.py

from django.shortcuts import render
from django.http import HttpResponse
from blog.models import Summary,Article
    
def get_index_page(request):
    all_ariticle = Summary.objects.all()
    return render(request,'blog/index.html',{'aritcle_list':all_ariticle})

实现文章详情页面跳转

Django中的超链接

1⃣️ 直接拼接

直接在a标签的href中拼接需要点击跳转的路径 在html的表现形式为

<a href="/blog/detail/{{item.summary_id}}"></a>
2⃣️ 参数以及命名空间
  • href后面是目标地址
  • templates中可以用"{% url 'app_name:url_name' param%}"
  • 其中app_name 和 url_name都在url中配置。
url函数的名称参数
  • 项目urls.py 写在include()的第二个参数位置,namespace='blog',还要在第一个参数中传入一个包含appname的元组 见下面demo
  • 应用下则写在url()的第三个参数位置,写上name=‘方法名’
  • 主要取决于是否使用include引用了另一个url配置文件
Demo
  • django项目的urls.py

      from django.contrib import admin
    from django.urls import path,include
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include(("blog.urls",'blog'),namespace='blog')),
    ]
  • django应用的urls.py

    from django.urls import path,include
    import blog.views
    urlpatterns = [
    path("hello_world",blog.views.hello_world),
    path("index",blog.views.get_index_summary_page),
    path("detail/<int:article_id>",blog.views.get_index_page,name='get_index_page')
    ]
  • django应用的views.py

    def get_detail_page(request,summary_id):
        all_ariticle = Summary.objects.all()
    cur_article = None
    for aritcle in all_ariticle:
    if aritcle.summary_id == summary_id:
    cur_article = aritcle
    break
    sectionList = cur_article.summary_content.split('\n')
    return render(request,'blog/detail.html',{'curr_aritcle':cur_article,'section_list':sectionList})
  • html超链接部分代码写法

     <a href="{% url 'blog:get_detail_page' item.summary_id %}"></a>
    

实现分页功能

参数request包含了一些get以及post的信息。

  • django应用的views.py

    def get_index_page(request):
        # 适用与/index?page=1之类的网络请求
    page = request.GET.get('page')
    if page:
    page = int(page)
    else:
    page = 1
    all_ariticle = Summary.objects.all()
    return render(request,'blog/index.html',{'aritcle_list':all_ariticle})

django分页组件Paginator

  • 在views.py中引入分页组件

    from django.core.paginator import Paginator
    def get_index_page(request):
    # 适用与/index?page=1之类的网络请求
    page = request.GET.get('page')
    if page:
    page = int(page)
    else:
    page = 1
    all_ariticle = Summary.objects.all()
    paginator = paginator(all_ariticle,3)
    page_aritcle_list = paginator.page(page)
    page_num = paginator.num_pages
    if page_aritcle_list.has_next():
    next_page = page + 1
    else:
    next_page = page
    if page_aritcle_list.has_previous():
    previous_page = page - 1
    else:
    previous_page = page
    return render(request,'blog/index.html',{
    'aritcle_list':page_aritcle_list,
    'page_num':range(1,page_num+1),
    "curr_page":page,
    'next_page':next_page,
    'previous_page':previous_page
    }
    )

补充部分 ORM

ORM

  • 对象关系映射(object relation mapping)
  • 实现了对象和数据库之间的映射
  • 隐藏了数据访问的细节,不需要编写sql语句

如何删除一个模型类

  1. 首先删除models.py中模型类的代码
  2. 删除模型类在migrations文件夹中对应的文件
  3. 删除django_migrations中对应的生成记录
  4. 再删除模型对应的数据表

切记不要直接把数据库整个删掉再生成,尤其是当数据库中有数据的时候。

如何导入数据

  1. django shell

    from blog.models import Aritcle
    >>> a = Aritcle()
    >>> a.title = "test django shell"
    >>> a.brief_content = "test django shell by maweefeng"
    >>> a.content = "test django shell new ariticle main content"
    >>> a.save()//保存到数据库
  2. 脚本批量导入

    import os
    import sys
    import random
    import django
    from datetime import date
    # 在当前项目中运行该python脚本
    project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(project_path)
    os.environ['DJANGO_SETTINGS_MODULE'] = 'Django_ORM.settings'
    django.setup()
    from orm.models import Aritcle
    #上面这行必须在django.setup()之后
    def import_data():
    for i in [1,2,3,4,5,6,7,8,9,10]:
    Aritcle.objects.create(title = '这个是标题%s'%(i),brief_content='这个是简介',content='这个是内容我们都有一个家名字叫中国')
    return True
    if __name__ == "__main__":
    if import_data():
    print('插入数据成功')
  3. fixtures
    使用django serialization 生成 model

    • 终端命令导出数据python3 manage.py dumpdata > orm.json生成了一个json文件

    • 然后从数据库中删除部分数据

    • 执行命令从json中导入数据 python3 manage.py loaddata orm.json

    • 数据库中重新出现了刚才删除的数据(因为从json中重新导入了数据)

  4. 数据库层面的导入数据

如何导出数据

  1. 导出fixtures能识别的json文件

    • 终端命令导出数据python3 manage.py dumpdata > orm.json生成了一个json文件
  2. 导出其他格式的文件
    pcharm自带的数据库工具,单选某个表右键dump data to file
    nivacate datum等数据库工具导出数据

  3. 数据库层面的导出数据

ModelsAPI

查询集介绍

  1. 查询 过滤 检索

    Aritcle.objects.all()#返回所有结果
    Aritcle.objects.get(title='这是一个标题')#返回一个结果 多条则会报错
    Aritcle.objects.filter(star__gte = 500)#queryset可以是多条结果 fans >=500的结果
  2. 字段数据匹配 大小写敏感

    teacher = Aritcle.objects.filter(star__in=[555,1231])
    teacher = Aritcle.objects.filter(title__contains='a')
    
  3. 结果切片 排序 链式查询

    Aritcle.objects.all()[:1]
    Aritcle.objects.all().order_by('star')#升序排序
    Aritcle.objects.all().order_by('-star')#降序排序
    Aritcle.objects.filter(star__gte = 500).order_by('-star')#链式查询
  4. 查看执行的原生SQL xxx.query

    print(str(Aritcle.objects.filter(star__gte = 500).order_by('-star').query))
    

返回新的QuerySet的API

  1. all() filter() order_by() exclude()去掉某个model reverse()反转 distinct()去重

    Aritcle.objects.all().exclude(title = 'title')
    Aritcle.objects.all().exclude(title = 'title').reverse()
    
  2. extra() defer() only() 实现字段别名 排除一些字段 选择一些字段

    Aritcle.objects.all().extra(select={'article_title'='title'})
    Aritcle.objects.all().only('title','star').query()
    
  3. dates() datetimes() 根据时间日期获取查询集

    Aritcle.objects.dates('publish_date','month',order ='DESC')
    
  4. union() intersection() difference() 并集 交集 差集

    a_500 = Aritcle.objects.filter(star__gte = 500)
    a_200 = Aritcle.objects.filter(star__lte = 200)
    print(a_500.union(a_200))
    print(a_500.intersection(a_200))
    print(a_500.difference(a_200))
  5. select_related() 一对一 多对一查询优化 prefetch_related() 一对多 多对多查询优化 反向查询

    Aritcle.objects.all().select_related('author')
    Audiences.objects.filter(star__gte = 500).prefetch_related('article')
    
  6. annotate() 使用聚合计数 求和 平均数 raw()执行原声sql

    # 对每个文章关联的作者的评🌟进行统计
    Aritcle.objects.values('author').annotate(vol=Sum('star'))
    # vol star 总和
    Aritcle.objects.values('author').annotate(ave=Avg('star'))
    # ave star 求平均值
  7. values() values_list() 获取字典或者元祖形式的queryset

    Aritcle.objects.values('title','star')
    Aritcle.objects.values_list('title','star')
    

不返回QuerySet的API

  1. 获取对象 get()获取单个对象 get_or_create()获取或者创建 first() last() latest() earliest() in_bulk()

  2. 创建对象 create() bulk_create() create_or_update() 创建 批量创建 创建或更新

  3. 更新对象 update() update_or_create()更新 更新或创建

    Aritcle.objects.filter(star__gte = 500).update(star=20)
    
  4. 删除对象delete() 使用filter过滤

    Aritcle.objects.filter(star__gte = 500).delete()
    
  5. 其他操作exists() count() aggregate()判断是否存在 统计个数 聚合

    Aritcle.objects.filter(star = 500).exists()
    Aritcle.objects.count()
    Aritcle.objects.aggregate(Max('star',Min('star'),Avg('star')))

自定义聚合查询 实现group_concat

在models.py 中自定义继承自models.Aggregate 的类GroupConcat

class GroupConcat(models.Aggregate):
    function = 'Group_Concat'
    template = '%(fuction)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'

    def __init__(self,expression,distinct=False,ordering=None,separator=',',**extra):
        super(GroupConcat,self).__init__(expression,
        distinct='DISTINCT' if distinct else '',
        ordering = 'ORDER BY %s' % ordering if ordering is not None else '',
        separator = 'SEPARATOR "%s"'%separator,
        output_field=models.CharField(),**extra)


views.py

Aritcle.objects.values('author').annotate(title=GroupConcat('title',distinct=True,ordering='title ASC',separator='-'))