0x00 前言

本文的主要学习资料来自以下网站

https://code.ziqiangxuetang.com/django # 自强学堂
https://docs.djangoproject.com/en/2.0/ # Django 2.0 官方文档
http://www.runoob.com/django/django-tutorial.html # runoob django

web开发的老师要求我们掌握django的。
总之自己未来的路子,不是安全工程师就可能是web全栈或者运维。
学学django我觉得还是很必要的。甚至以后出web题目的时候可以用py写

0x01 创建第一个django项目

我极力推荐用venv的虚拟环境进行配置,解决django的版本问题。我学习的应该是2.0版本

命令行

创建项目

>> django-admin startproject project_name 

新建APP

>> django-admin startapp app_name

新建数据库

似乎会报错?让我在研究一下

使用开发服务器

oython manage.py runserver <port>

环境终端

python manage.py shell

做完这些事情后,将新定义的app放入settings中
我定义了一个app叫做mysite

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
##
    'mysite'
]

备注:这一步是干什么呢? 新建的 app 如果不加到 INSTALL_APPS 中的话, django 就不能自动找到app中的模板文件(app-name/templates/下的文件)和静态文件(app-name/static/中的文件) , 后面你会学习到它们分别用来干什么.

pycharm

新建工程,django工程。
pycharm会为你自动创建venv的环境。
对于小白来说还是pycharm方便啊

0x02 输出hello world

定义视图函数(访问页面内容)

打开 views.py

# coding=utf-8
from django.shortcuts import render
from django.http import HttpResponse


# Create your views here.

def index(request):
    return HttpResponse(u"hello world!<br> 我的第一个django项目")

当网页发来请求的时候,就会返回一个response的对象。

规定什么网页访问什么内容

在urls.py中添加如下语句
2.0版本已经不用正则表达式匹配了

from django.contrib import admin #默认
from django.urls import path #默认

from my_rst_django_app import views as helloworld # 强烈建议包含
urlpatterns = [
    path('admin', admin.site.urls),
    path('',helloworld.index) #这样添加
]

run !

截图留念!

Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

0x03 request 请求

在php中,通过

$_GET["sth."]

能够获取get参数,django是如何实现的呢?

在views文件中处理http请求。

在views.py增加如下函数

def hello(request):
    username = request.GET["name"]

    result = "Hello {name}".format(name=username)

    return HttpResponse(result)

在url文件中包含该函数

在urls中增加如下语句

from my_rst_django_app import views
urlpatterns = [
    path('admin', admin.site.urls),
    path('hello/', views.hello,name=hello1),  # new
]

预览效果

Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

更加优雅的网址 /hello///..

django支持更加优雅的url,增加hello2 函数如下

def hello2(request, name1, name2):
    result = "Hello {name1} and {name2}".format(name1=name1 , name2=name2)
    return HttpResponse(result)

在urlpatterns中增加数组如下

path('add/<str:a>/<str:b>/', views.hello2, name='hello2'),

(变量类型和python中的相似,比如str,list,int)
获得的效果如下

http://127.0.0.1:8000/hello2/1/1/
Hello 1 and 1

URL name 详解

还记得那个name么?

    path('hello/', views.hello,name='hello1'),  # new
    path('hello2/<str:name1>/<str:name2>/', views.hello2, name='hello2')

所以,这个name是要来干嘛的呢?
简单说,name 可以用于在 templates, models, views ……中得到对应的网址,相当于“给网址取了个名字”,只要这个名字不变,网址变了也能通过名字获取到。

pass 之后补充。

Django Templates

request的返回的东西,不一定是一个字符串,当然可以是一个html网页。我们可以把内容写到一个html中。然后将它放到templates的文件夹中。pycharm很好心的帮你把这个文件夹涂成了紫色。

建立一个普通的html模板

把文件放到templates中

<!DOCTYPE html>
<html lang="CN">
<head>
    <meta charset="UTF-8">
    <title>Shaobaobao的第一个django项目</title>
</head>
<body>

<a href = "./hello2/1/1">欢迎1,1,小伙伴</a>

</body>
</html>

利用render函数包含它

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

Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog
点击href后就会显示

Hello 1 and 1

那么python能够像php那样随意操控模板中的变量嘛?答案是肯定的

django模板进阶

传递参数与简单 for 循环

传递字符串

传递模板的参数是个字典。就好像php之间利用序列化字符串传递一样。

def index(request):
    string=u"Shaobaobao正在努力学习django"
    return render(request,'home.html',{'string':string})

string不是类型,是名称。

在html中利用{{}}调用传进来的字符串
如果要调用传进来的参数,那就可以用{{}}
是不是和vue.js有点像呢?

<body>
{{string}}
</body>
Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

传递列表
列表当然是可以传的,就是处理起来要花一些功夫。

一下是列表的标准语法:
人性化的pycharm已经帮你解决了所有复杂的问题

{% for i in menu %}
<ul>{{ i }}</ul>
{% endfor %}

传递字典
至于字典呢?其语法应该是这样子的

<ul>
{% for key,value in article.items %}
    <li>{{ key }}==>{{ value }}</li>
{% endfor %}
</ul>

最后放上效果:

Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

简单逻辑判断

模板只能完成一些简答的逻辑运算,对于加减乘除则不支持


{% if var >= 90 %} 成绩优秀,自强学堂你没少去吧!学得不错 {% elif var >= 80 %} 成绩良好 {% elif var >= 70 %} 成绩一般 {% elif var >= 60 %} 需要努力 {% else %} 不及格啊,大哥!多去自强学堂学习啊! {% endif %}

网页链接

利用url可生成网页链接。
但是生成的url是\<>\<>形式的

<a href="{% url "hello2" "bill" "bill2"%}">点击这里</a>

变量request.xxx

在pycharm新建项目的时候已经给你加好所有配置了
这边就放点小例子


当前用户 {{ request.user }} <br> {% if request.user.is_authenticated %} {{ request.user.username }},您好! {% else %} 请登陆,这里放登陆链接 {% endif %} 当前网址 {{ request.path }} <br> 当前GET请求 {{ request.GET.urlencode }} <br> 当前用户 AnonymousUser 当前网址 / 当前GET请求

0x04 连接数据库

pycharm工程 database配置

在django工程的左侧有数据库,首次访问需要输入密码,并测试connect
之后就能使用了

setting文件配置

在setting.py中更改database部分如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django', #库名
        'USER':'root',
        'PASSWORD':'',
        'HOST':'localhost',
        'PORT':'3306',
    }
}

生成数据库

在models.py中添加类名:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()
    def __str__(self):
        return "name is "+self.name+"\nage is "+str(self.age)
        #定一个魔法函数,之hi后用到

然后用以下命令初始化数据库

python manage.py makemigrations
python manage.py migrate

我用的mysql 爆出了以下HINT

HINT: MySQL's Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it. See: https://docs.djangoproject.com/en/2.0
/ref/databases/#mysql-sql-mode

django希望mysql开启严格模式(多半是注入被注入怕了)
按照官方文档说的做做也不错

数据库API

创建语句

from my_rst_django_app.models import Person
>>> Person.objects.create(name="bill",age=24)
<Person: Person object (1)>

能创建的数据类型很多,在pycharm中打.Field有惊喜

来看看数据库发生了什么?

+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | bill |  24 |
+----+------+-----+

新建一个对象的方法有以下几种:

  1. Person.objects.create(name=name,age=age)
  2. p = Person(name="WZ", age=23)
    p.save()
  3. p = Person(name="TWZ")
    p.age = 23
    p.save()
  4. Person.objects.get_or_create(name="WZT", age=23)

查询语句

Person.objects.get()
# select * from `Person`
# 括号中的东西相当于where子句
<Person: name is bill   age is 24>
# 如果不好好处理的话,返回的结果是两个会报错,需要用all才行
>>> Person.objects.all()
<QuerySet [<Person: name is billage is 24>, <Person: name is shaobaoage is 2>]>

获取对象有以下方法:

  1. Person.objects.all()
  2. Person.objects.all()[:10] 切片操作,获取10个人,不支持负索引,切片可以节约内存
  3. Person.objects.get(name=name)

更加精细的查找子句

## get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter
Person.objects.filter(name="abc")  # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人
Person.objects.filter(name__iexact="abc")  # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件
Person.objects.filter(name__contains="abc")  # 名称中包含 "abc"的人
Person.objects.filter(name__icontains="abc")  #名称中包含 "abc",且abc不区分大小写

Person.objects.filter(name__regex="^abc")  # 正则表达式查询
Person.objects.filter(name__iregex="^abc")  # 正则表达式不区分大小写

## filter是找出满足条件的,当然也有排除符合某条件的
Person.objects.exclude(name__contains="WZ")  # 排除包含 WZ 的Person对象
Person.objects.filter(name__contains="abc").exclude(age=23)  # 找出名称含有abc, 但是排除年龄是23岁的
'''
加了i 就相当于不区分大小写查找
默认是binary的方式
'''

一下是django的命名要求

name 和 age 等字段中不能有 __(双下划线,因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等)
也不能有Python中的关键字,name 是合法的,student_name 也合法,但是student__name不合法,try, class, continue 也不合法,因为它是Python的关键字( import keyword; print(keyword.kwlist) 可以打出所有的关键字)

数据库辅助插件 South

Django 的第三方 app South 就是专门做数据库表结构自动迁移工作,Jacob Kaplan-Moss 曾做过一次调查,South 名列最受欢迎的第三方 app。事实上,它现在已经俨然成为 Django 事实上的数据库表迁移标准,很多第三方 app 都会带 South migrations 脚本,Django 1.7 中集成了 South 的功能。

在2.0的版本中south已经被添加了。在建立数据库的时候会被自动添加

0x05 后台

在models建立如下表格并导入数据库

class Article(models.Model):
    title = models.CharField(u'标题', default="notitle", max_length=256)
    content = models.TextField(u'内容', max_length=5000, default="nothing")
    up_load_time=models.DateTimeField(null=True,blank=True)
    last_edit_time = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return self.title
# 定义str后,在数据库显示的东西就有标题了

在admin.py中添加如下代码

from django.contrib import admin
from .models import Article

admin.site.register(Article)
#允许admin操控register表

超级管理员

接下来需要创建超级用户的账号密码
依次在PyCharm的控制台下输入如下3个命令:

python manage.py shell  
from django.contrib.auth.models import User  
user=User.objects.create_superuser('用户名','邮箱','密码')  

之后输入/admin就能进入后台管理列表了。
有点像phpmyadmin
当然,这个超级管理员后台更加注重管理用户

另一种方式

python manage.py createsuperuser

之后会提示你输入各种信息。
利用第一种方法添加的管理员,可以设置各种各样的密码
但是通过这种命令输入的密码,有比较严苛的要求。

管理员后台

from django.contrib import admin
from .models import Article
admin.site.register(Article)

这样admin就可以管理Article列表
其实界面还是很不错的
Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

自定义管理员后台

from django.contrib import admin
from .models import Article

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'up_load_time', 'last_edit_time',)

admin.site.register(Article, ArticleAdmin)

这样,在admin页面中就可以显示除了标题之外的其他信息了。

那么,如果想要显示一些更加灵活的信息呢?
比如说在数据库中存储着一个人的姓和名,显示要显示出姓名,如何做到呢?

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def my_property(self):
        return self.first_name + ' ' + self.last_name

    my_property.short_description = "Full name of the person"
    full_name = property(my_property)

自行定义了full_name段,然后用property函数来加入进来。记得用.short_description 来告诉别人这个是干嘛的

分权查看后台

定制加载的列表, 根据不同的人显示不同的内容列表,比如输入员只能看见自己输入的,审核员能看到所有的草稿,这时候就需要重写get_queryset方法

class MyModelAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyModelAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        else:
            return qs.filter(author=request.user)

该类实现的功能是如果是超级管理员就列出所有的,如果不是,就仅列出访问者自己相关的

更多自定义的功能

更加灵活的过滤器 ———— fliter
https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
更加灵活的字段定义 ———— field_set
https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets
别的功能翻文档吧?

0x06 表单

表单类 djiango.forms

新建表单变量。
建立一个form.py(app目录下)

from django import forms

class AddForm(forms.Form):
    a = forms.IntegerField()
    b = forms.IntegerField()

这样,我们就建立一个表单类,可以在html模板中套用这个类。django提供了默认的渲染引擎也html代码模板帮助我们完成一系列操作。

Django 的 forms 提供了:

  • 模板中表单的渲染
  • 数据的验证工作,某一些输入不合法也不会丢失已经输入的数据。

还可以定制更复杂的验证工作,如果提供了10个输入框,必须必须要输入其中两个以上,在 forms.py 中都很容易实现
据说 也有一些将 Django forms 渲染成 Bootstrap 的插件,也很好用,很方便。现在我还不会用

GET方法

在视图函数中,导入AddForm类,完成GET方法!

def get_test(request):
    if request.method == 'GET':
        form = AddForm(request.GET)

        if form.is_valid():
            a = form.cleaned_data['a']
            b = form.cleaned_data['b']
            return HttpResponse(str(int(a) * int(b)))
    else:
        form = AddForm
    return render(request, 'get_test.html', {'form': form})

有所不同的是,传入html模板的参数是一个form类,这当然是合法的。
新建html模板 get_test如下,现在 form类型被包到了<form>表单中

<body>
    <form method="get">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="submit">
    </form>
</body>

在url.py中加入新的网页并查看

    path('get_test/', views.get_test, name="get_test"),

Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog
默认的并不是这么好看。但是已经完成了可以完成的功能了。

POST 方法

代码基本相同,html甚至可以用一个板子('get'==>'post')

def post_test(request):
    if request.method == 'POST':
        form = AddForm(request.POST)

        if form.is_valid():
            a = form.cleaned_data['a']
            b = form.cleaned_data['b']
            return HttpResponse(str(int(a) + int(b)))
    else:
        form = AddForm
    return render(request, 'post_test.html', {'form': form})

可以自己尝试一下,我觉得很有意思~
Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

0x07 Django Setttings

国际化

Django 支持国际化,多语言。Django的国际化是默认开启的,如果您不需要国际化支持,那么您可以在您的设置文件中设置 USE_I18N = False,那么Django会进行一些优化,不加载国际化支持机制。
NOTE: 18表示Internationalization这个单词首字母I和结尾字母N之间的字母有18个。I18N就是Internationalization(国际化)的意思。

开启国际化设置,在settings.py中修改如下:

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.locale.LocaleMiddleware',
)


LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

LANGUAGES = (
    ('en', ('English')),
    ('zh-cn', ('中文简体')),
    ('zh-tw', ('中文繁體')),
)

#翻译文件所在目录,需要手工创建
LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    "django.core.context_processors.i18n",
)

DEBUG选项

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

路径选项

这里用到了python中一个神奇的变量 file 这个变量可以获取到当前文件(包含这个代码的文件)的路径。os.path.dirname(file) 得到文件所在目录,再来一个os.path.dirname()就是目录的上一级,BASE_DIR 即为 项目 所在目录。

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

允许访问的域名

ALLOWED_HOSTS 允许你设置哪些域名可以访问,即使在 Apache 或 Nginx 等中绑定了,这里不允许的话,也是不能访问的。
当 DEBUG=False 时,这个为必填项,如果不想输入,可以用 ALLOW_HOSTS = ['*'] 来允许所有的。

ALLOWED_HOSTS = []
#  ['*.besttome.com','www.ziqiangxuetang.com']

把模板文件放在别的地方

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR,'templates'),
            os.path.join(BASE_DIR,'templates2'),#添加
        ],
        'APP_DIRS': True,
]

静态文件部署
我把自强学堂的setting静态配置搞过来看看~

觉得解释的很详细。


# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' # 当运行 python manage.py collectstatic 的时候 # STATIC_ROOT 文件夹 是用来将所有STATICFILES_DIRS中所有文件夹中的文件,以及各app中static中的文件都复制过来 # 把这些文件放到一起是为了用apache等部署的时候更方便 STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static') # 其它 存放静态文件的文件夹,可以用来存放项目中公用的静态文件,里面不能包含 STATIC_ROOT # 如果不想用 STATICFILES_DIRS 可以不用,都放在 app 里的 static 中也可以 STATICFILES_DIRS = ( os.path.join(BASE_DIR, "common_static"), # '/path/to/others/static/', # 用不到的时候可以不写这一行 ) # 这个是默认设置,Django 默认会在 STATICFILES_DIRS中的文件夹 和 各app下的static文件夹中找文件 # 注意有先后顺序,找到了就不再继续找了 STATICFILES_FINDERS = ( "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder" )

为了完成测试,我在目录下建立文件如下
Python Django 学习笔记(基础篇)-ShaoBaoBaoEr's Blog

这样的话,两张图片都能被找到。同理能够运用到CSS,JS,JQ等文件

http://127.0.0.1:8000/static/img/neko.jpg
http://127.0.0.1:8000/static/img/neko2.jpg

引用静态文件
在html中放入一些代码,就可以把香子兰的照片放到网上

{% load static %} 
<!--这个必须写-->
<img src={% static "img/neko.jpg" %}>