django从传说到入门

1. 介绍

我们从开发环境的安装,到创建项目,编写前后端代码开始了解一下django大概是什么样子的。
看一下django的文档其实就知道,django很强大,但是同时细节也真的非常多,同时相同的功能可能也有不同的实现。我们本着入门了解的态度,先看一下它大致是什么样子的。

2. 安装虚拟环境

python使用virtualenv来管理虚拟环境,它可以给当前的python程序一个隔离的依赖环境,在这个环境中安装依赖,使用依赖就都不依赖全局了,可以很好的解决多个项目依赖不同的情况。
使用命令pip install virtualenv安装。

virtualenv安装

安装后,我们创建目录trydjango,然后cd到目录中运行 virtualenv -p python3 . 来在当前目录下面创建一个依赖目前系统中的python3版本的虚拟环境。具体使用哪个版本的python就看主机上具体的安装情况了。

命令执行后,会创建如下内容:


环境创建好后,使用命令source bin/activate就可以进入虚拟环境中。这时所有的依赖和python版本就都是当前虚拟环境中的了,跟系统就隔离开了。


如图,前面出现了 (trydjango) 就说明已经进入了虚拟环境,如果需要离开环境,使用 deactivate命令即可。

3. 安装Django

在虚拟环境中直接运行 pip install Django即可(其他方式可以参考文档)
安装之后,需要重新运行一次source bin/activate,才能使用django安装时安装的一些命令,比如django-admin。

4. 初始化项目

创建一个文件夹叫src,然后cd进入src,再执行命令django-admin startproject firstapp . 通过这个命令来初始化一个项目,初始化完成后,会出现manage.py文件和firstapp文件夹。

5. 运行服务

这时我们就可以通过执行命令python manage.py runserver来启动django服务了


这里面红色的警告是因为没有初始化数据库造成的。

服务启动后,访问启动服务时给出的服务地址http://127.0.0.1:8000/,出现如下界面就说明服务启动成功了。

6. 初始化数据库

django需要连接数据库才能正常进行使用,我们通过如下命令来初始化数据库。
python manage.py migrate

image.png

7. 新建自己的app

app是什么我们后面再说,现在先放一下。

执行命令python manage.py startapp products来创建一个名叫products的app,
命令执行后,会创建一个products的文件夹:

创建完app之后,要记得在settings.py里面注册我们的app,这个app才能被使用,我们修改里面对应的代码,增加products的app声明

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'products',  # 我们新增的app,前面都是默认创建的app
]

7.1 创建模型

a. 修改模型文件,创建一个Product模型
修改models.py, 代码如下:

from django.db import models

# Create your models here.
class Product(models.Model):
    title = models.TextField()
    description = models.TextField()
    price = models.TextField()

b. 创建移植脚本
每次修改模型文件之后,都要先运行python manage.py makemigrations 来生成移植脚本,这个脚本就是用来把我们对模型做出的改变,同步到数据库用的。

然后再运行python manage.py migration命令来移植变化,这样数据库结构就会同步成我们新的模型的样子。

7.2 尝试修改模型

修改models.py, 增加一个summary字段,代码如下:

from django.db import models

# Create your models here.
class Product(models.Model):
    title = models.TextField()
    description = models.TextField()
    price = models.TextField()
    summary = models.TextField()

再次运行python manage.py makemigrations, 会自动生成更新文件


然后我们再次运行一下 python manage.py migration就可以把变化同步到数据库。

7.3 操作模型

Django默认提供了一些app给我们使用,其中admin就是一个。通过admin我们可以对模型、用户进行一些基本的管理,我们可以把我们的Product模型注册到admin中,然后通过admin来对它进行管理。
a. 在admin里面注册模型
修改admin.py,注册刚才创建的Product模型

from django.contrib import admin
from .models import Product

# Register your models here.
admin.site.register(Product)

b. 通过网页服务操作模型
前面我们已经启动了服务。我们访问http://127.0.0.1:8000/admin/ ,会进入一个登陆界面:


这时我们是没有用户的,我们需要到命令行去创建一个系统的用户出来。
执行命令 python manage.py createsuperuser 来创建超级用户(系统第一个用户),按照步骤创建即可:

警告是因为密码太短造成的
然后我们回到登录界面,使用刚才创建的用户名密码登录界面,就可以看到我们新增的模型了:

通过界面就可以进行对模型的各项操作。

c. 通过命令行操作模型
除了通过界面来操作模型之外,我们也可以通过在命令行打开一个shell来进入python的执行环境,然后直接操作模型。
在命令行运行命令python manage.py shell可以打开一个shell,我们在shell里面就可以引入模型,然后操作模型,如下:


基本就是引入Product之后,通过Product.objects上面的方法来进行各种操作。
在几个新增操作后,我们也可以回到上面提到的网页服务来查看对象列表:

7.4 修改模型

模型定义好之后,有可能还需要进行修改,我们尝试把模型修改如下:

from django.db import models

# Create your models here.
class Product(models.Model):
    title = models.CharField(max_length=64)  # CharField必须填写max_length属性
    description = models.TextField()
    price = models.DecimalField(decimal_places=2, max_digits=10000)  # DecimalField必须有这两个属性,否则报错
    summary = models.TextField(blank=True, null=False) # blank控制界面上是否允许为空的检查,如果是True,就不检查空值,  null控制如果数据是空时,应该传什么值给数据库

修改好后还是执行 python manage.py makemigrations , python manage.py migrate命令来使改动生效。
这时如果刷新列表界面或者使用命令行Product.objects.all()查看数据可能会报错,那是因为我们修改了字段类型,原有的数据类型不对,导致出错。这时我们在命令行执行Product.objects.all().delete()删除旧数据即可。

8. 界面

8.1 创建一个新的app

创建一个新的app并不是必要的操作,我们可以在上面的products里面继续创建界面,只是为了方便起见,我们再建一个app来做下面的操作。
使用命令创建pages app python manage.py startapp pages。创建之后记得要在settings.py中注册新的app:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'products',
    'pages',
]

8.2 编写界面文件

下面我们先修改一下网站的首页,修改首页需要如下几个步骤
a. 编写界面文件pages/views.py

from django.shortcuts import render
from django.http import HttpResponse # 引入HttpResponse来返回请求

# Create your views here.
def home_view(request, *args, **kwargs):
    print(request.user) # request里面可以查看用户会话信息
    return HttpResponse("

this is the homepage

") # 返回一段html

b. 配置路由信息
路由信息通过查看settings.py里面的ROOT_URLCONF配置得知使用的配置文件是 firstapp.urls


也就是这个文件:

我们在这个文件中配置我们新写的首页内容,内容如下:

from django.contrib import admin
from django.urls import path
from pages.views import  home_view

urlpatterns = [
    path('', home_view),
    path('admin/', admin.site.urls),
]

在这个文件里,我们导入了新写的界面文件,然后注册了路由,这时访问服务即可看到效果:



同理,如果需要增加其他页面,一样操作即可。

8.3 编写templates而不是在python中直接返回字符串

在views.py中,可以通过render方法来渲染一个模板来返回请求

from django.shortcuts import render
from django.http import HttpResponse # 引入HttpResponse来返回请求

# Create your views here.
def home_view(request, *args, **kwargs):
    print(request.user)
    # return HttpResponse("

this is the homepage

") # 返回一段html return render(request, 'home.html', {})

为了让home.html正确渲染,我们需要做两件事,一个是创建home.html,另外一个是把它注册到Django中。
a. 创建home.html


内容如下:




    
    
    
    Document


    

home page

当前登录的用户是: {{request.user}}

我们创建templates目录,然后在其中新建home.html文件,在html文件中,我们获取了一下当前登录的用户。

b. 注册模板文件
模板文件在settings.py中进行注册,配置DIRS进行注册模板目录,代码如下:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  # 这里就是存放template的目录
        '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',
            ],
        },
    },
]

现在我们刷新界面,就可以看到我们的模板正确被渲染了:


8.4 模板相关的一些用法

8.4.1 模板继承

对于一个有很多页面的应用来说,经常会遇到结构固定,但是内容各异的情况,这种时候通过模板继承的方式,我们就可以很好的来复用定制好的结构,在每一个详细的页面中,可以只关心需要替换掉的部分。
a. 我们在templates目录中新建base.html文件,内容如下:




    
    
    
    Document


    

base.html中的内容1

{% block content %}

默认内容

{% endblock %}

base.html中的内容2

{% block bottom %}

默认bottom

{% endblock %}

base.html中的内容3

{% block other %}

默认other信息

{% endblock %}

其中

{% block content %}
        

默认内容

{% endblock %}

类似结构部分是在模板中定义的可替换区域,block和endblock定义了一个内容的替换区域, content是区域的名称(用于多个block存在的情况下,指定继承模板时继承的是一块),block和endblock中间的内容就是没有替换内容的情况下,默认显示的内容。

我们修改home.html为如下内容:

{% extends 'base.html' %}

{% block content %}

当前登录的用户是: {{request.user}}

{% endblock content%}

外部的信息

{% block bottom %}

底部块的信息

{% endblock bottom %}

在这个文件中,我们通过 {% extends 'base.html' %}指定了我们要继承的模板,然后下面两个block都通过名称指定了要覆盖的内容,中间的

外部的信息

是出现在继承了其他模板的模板中的独立代码,这一部分是不会被执行的,我们继承模板的时候,只能去覆盖block部分的内容。

最终界面如下显示:


8.4.2 导入其他模板内容

还有一种复用模板的情况是,我们把一些写好的模板整个导入到我们的模板中,这样可以把一些界面元素通过合理的拆分,变成单一的文件,自己管自己的业务,易于管理。
我们在templates目录下新建components目录,然后在components目录新建navbar.html


然后修改base.html,在其中引入navbar.html




    
    
    
    Document


    
    {% include 'components/navbar.html' %}

    

base.html中的内容1

{% block content %}

默认内容

{% endblock %}

base.html中的内容2

{% block bottom %}

默认bottom

{% endblock %}

base.html中的内容3

{% block other %}

默认other信息

{% endblock %}

最终效果如下:


navbar引入的效果

8.4.3 给模板传入上下文参数

views.py 中的 render方法中可以传入第三个参数,这个参数是一个dictionary,每一个dictionary的key都会变成模板中的一个变量供模板使用。
新建一个about.html,然后在views.py中渲染这个模板, 并且在urls.py中注册这个view,最终查看效果。
a. views.py中新增一个about_view

from django.shortcuts import render
from django.http import HttpResponse # 引入HttpResponse来返回请求

# Create your views here.
def home_view(request, *args, **kwargs):
    print(request.user)
    # return HttpResponse("

this is the homepage

") # 返回一段html return render(request, 'home.html', {}) def about_view(request, *args, **kwargs): context = { 'name': 'hushi', 'age' : 33 } return render(request, 'about.html', context)

b. 在urls里面注册这个view

from django.contrib import admin
from django.urls import path
from pages.views import  home_view, about_view

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home_view),
    path('about/', about_view)
]

c. 编写about.html

{% extends 'base.html' %}

{% block content %}

当前登录的用户是: {{request.user}}

{% endblock content%} {% block bottom %}

my name is {{name}}, and I'm {{age}} years old

{% endblock bottom %}

这里我们通过 {{ }}来使用上下文中传入的参数,最终看到的效果是:

参数传入上下文

8.4.4 for循环

我们修改about.html,在其中加入for循环的代码:

{% extends 'base.html' %}

{% block content %}

当前登录的用户是: {{request.user}}

{% endblock content%} {% block bottom %}

my name is {{name}}, and I'm {{age}} years old

my friends are

    {% for friend in friends %}
  • {{forloop.counter}} - {{friend}}
  • {% endfor %}
{% endblock bottom %}

这一段代码就是for循环的主要语法:

{% for friend in friends %}
  • {{forloop.counter}} - {{friend}}
  • {% endfor %}

    其中forloop.counter是循环的下标。

    我们在views.py中传入参数查看一下效果:

    def about_view(request, *args, **kwargs):
        context = {
            'name': 'hushi',
            'age' : 33,
            'friends' : [
                'Jim',
                'Alex',
                'Joe'
            ]
        }
        return render(request, 'about.html', context)
    
    image.png

    8.4.5 条件

    条件判断比较好理解,我们这里就给出基本的写法

    {% if some_condition %}
    do something
    {% elif other_condition %}
    do sth
    {% else %}
    blah blah blah
    {% endif %}
    

    8.4.6 模板中的tag与filter

    a. tag:
    在模板中,我们已经使用了if、include、extends、block等等tag,这些概念在模板中就叫做tag,django默认提供了很多tag,可以参考文档:https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#
    b. filter:
    filter是一些数据操作的方法,我们把数据输入进去,然后再输出一些数据,用法比较简单
    {{ value| filter1 | filter2| ... }}
    类似linux的管道运算符,把前面的值传给第一个filter,然后再把结果传给第二个filter...
    具体可以参考文档:https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#built-in-filter-reference

    8.5 从后端获取数据并展示

    上面我们已经了解了如何给模板传递上下文参数,那么如何从后端获取数据并显示其实就非常简单了。具体的思路就是在views.py对应的视图方法中,通过models.py中定义的模型上的方法来获取数据库中的数据,然后通过上下文参数传递给模板
    我们以Product的展示为例:
    a. 在products的app目录中的views.py中新增展示Product详情的方法:

    from django.shortcuts import render
    from .models import Product
    # Create your views here.
    def product_detail_view(request, *args, **kwargs):
        # context参数只能接受dictionary对象
        return render(request, 'products/detail.html', {
            'object': Product.objects.get(id='5')
        })
    

    (第三个参数把id=5的Product对象封装到了一个dictionary对象中,这是因为传给模板的这个参数只接受dictionary类型的对象)

    b. 在urls.py里面注册这个新的view

    from django.contrib import admin
    from django.urls import path
    from pages.views import  home_view, about_view
    from products.views import product_detail_view
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', home_view),
        path('about/', about_view),
        path('product', product_detail_view)
    ]
    

    c. 编写detail.html
    在templates里面新建products目录,然后新建detail.html

    {% extends '../base.html' %}
    
    {% block content %}
    

    {{ object.title }}

    price is {{ object.price }}

    {% endblock content %}

    这里我们还是保持之前的模板继承写法。最终效果如下:


    普通对象和dictionary对象的转换
    上面我们为了把Product对象转换成dictionary对象,而封装了一层,这就导致我们需要在模板里面每次引用属性的时候都要增加一层object.的开头,所以最好的还是能有一种方法能快速的把Product对象直接转换成dictionary对象。 还真有:
    我们重写一下products/views.py 以及 templates/products/detail.html

    from django.shortcuts import render
    from .models import Product
    # Create your views here.
    def product_detail_view(request, *args, **kwargs):
        # context参数只能接受dictionary对象
        # return render(request, 'products/detail.html', {
        #     'object': Product.objects.get(id='5')
        # })
        return render(request, 'products/detail.html', Product.objects.get(id='5').__dict__)
    

    直接使用Product对象的__dict__属性,这样模板里面就可以直接使用属性名称了:

    {% extends '../base.html' %}
    
    {% block content %}
    

    {{ title }}

    price is {{ price }}

    {% endblock content %}

    8.6 关于app


    我们在前面创建过pages和products两个app,通过文件结构其实也很容易看出来,一个app其实就是一个可以被复用的完整的模块,这个模块完整的包含模型,界面,测试等等。
    但是目前我们的app有一部分在内部,有一部分还在外部,其实更好的做法是做一定的解耦。
    a. 界面的解耦
    目前我们把模板文件都写到了外面的templates文件夹中,实际上我们可以把属于app的模板都写在app自己文件夹下面对应的templates目录里面,因为我们在settings.py里面我们做了APP_DIRS: True的配置:

    那么如果两个位置都存在相同名称的模板,哪个会生效呢?其实我们思考一下就应该知道,app是模块,模块是被组合使用的,而外面的templates(也就是我们在settings.py的TEMPLATES里面配置的DIRS)是整个project里面的代码,所以它的优先级会高,会覆盖app里面的模板。

    我们故意把路径拼错来看一下报错信息,里面已经写清楚了Django寻找模板的路径和顺序:


    b. 路由的解耦
    界面解耦之后,其实就剩下urls.py里面有对app结构的耦合了,但是我们查看一下admin的路由配置就不难发现,实际上也是有解耦办法的



    通过这种方式,app可以只暴露一个信息,通过这一个路由信息,来跟project组合起来,而不是需要暴露app里面的方法、路径等等具体的实现。

    8.7 表单

    我们在views.py里面新增一个表单的view,如果是get请求,就返回一个空的表单,如果是表单被post请求提交,那么我们就检查表单内容是否有效,如果有效就把表单保存到数据库,然后再返回一个新的空表单对象给界面。

    a. 在products目录里面新建forms.py, 创建输入三个属性的一个form

    from django.forms import ModelForm, Textarea
    from .models import Product
    
    class ProductForm(ModelForm):
    
        class Meta:  
            # 指定我们的form是针对Product模型的
            model = Product
            # 指定
            fields = [
                'title',
                'description',
                'price'
            ]
            # 覆盖默认的控件
            widgets = {
                'description': Textarea(attrs={'cols': 80, 'rows': 20}),
            }
    

    b. 在products/views.py里面增加product_create_view用来新建Product

    from django.shortcuts import render
    from .models import Product
    from .forms import ProductForm
    # Create your views here.
    def product_detail_view(request, *args, **kwargs):
        # context参数只能接受dictionary对象
        # return render(request, 'products/detail.html', {
        #     'object': Product.objects.get(id='5')
        # })
        return render(request, 'products/detail.html', Product.objects.get(id='5').__dict__)
    
    
    def product_create_view(request, *args, **kwargs):
        form = ProductForm(request.POST or None)  # 使用request.POST对象或者None来初始化Form里面的内容, 如果需要编辑一个数据库对象,那么需要传入第二个参数 instance=要编辑的对象
        if form.is_valid():  # 如果表单通过了校验
            form.save()      # 保存表单内容到数据库
            form = ProductForm()  #给界面重新初始化一个空的form对象,以重新填写下一个表单
    
        context = {
            'form' : form
        }
        return render(request, 'products/create.html', context)
    
    

    c. 创建form的界面模板,在templates/products/里面创建create.html

    {% extends '../base.html' %}
    
    {% block content %}
    
    {% csrf_token %} {% comment %} 因为使用了post去提交表单,所以需要提供csrf_token {% endcomment %} {{ form.as_p }} {% comment %} form.as_p 会把form里面的每一个字段用

    标签封装起来 {% endcomment %}

    {% endblock content %}

    d. 在urls里面增加product_create_view的路由:

    from django.contrib import admin
    from django.urls import path
    from pages.views import  home_view, about_view
    from products.views import product_detail_view, product_create_view
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', home_view),
        path('about/', about_view),
        path('product', product_detail_view),
        path('create', product_create_view)
    ]
    

    e. 访问地址,可以新建一个product对象


    image.png

    f.点击save后,我们到admin的后台去确认一下是否增加成功:


    image.png

    你可能感兴趣的:(django从传说到入门)