zoukankan      html  css  js  c++  java
  • Python-Django 第一个Django app

    第一个Django app

     

    by:授客 QQ:1033553122

    测试环境:

    Python版本:python-3.4.0.amd64

    下载地址:https://www.python.org/downloads/release/python-340/

     

     

    Win7 64位

     

    Django  1.11.4

    下载地址:https://www.djangoproject.com/download/

     

     

    安装django

    python setup.py install

     

    测试是否成功

    >>> import django

    >>> print(django.get_version())

    1.10.6

    >>>

     

    或者

    python -m django --version

    1.10.6

     

    参考连接:

    https://docs.djangoproject.com/en/1.10/intro/install/

    https://docs.djangoproject.com/en/1.10/intro/tutorial01/

     

     

    第一个 Django app Part1

    新建项目,选择存放项目的目录(例F:projectDjangoFirstApp),进入该目录,执行django-admin命令

    例:新建mysite项目

    C:Userslaiyu>cd /d F:projectDjangoFirstApp

    F:projectDjangoFirstApp>django-admin startproject mysite

     

    注意:项目名称不能和python内置组件,或Django组件命名项目,特别是django(和Django自身冲突)或test(和python内置模块冲突)。

     

    运行命令后,生成文件如下:

    FirstApp/

       mysite/

           manage.py

           mysite/

              __init__.py

              settings.py

              urls.py

              wsgi.py

     

    说明:

    最外层mystie: 项目根目录。

     

    manage.py:提供各种方式同Django项目交互的命令行工具。

     

    内层的mysite:python包,存放项目python文件目录。

     

    mystie/__init__.py:一个空文件,告诉python,该目录应该被解析为python包

     

    mysite/settings.py:包含Django项目的配置/设置。

     

    mysite/urls.py:包含Django项目的url声明。

     

    mysite/wsgi.py:服务项目的WSGI-compatible。

     

     

    确认项目是否能正常运行,切换到根目录,即例中的mysite,运行如下命令:

    F:projectDjangoFirstAppmysite>python manage.py runserver

     

    运行结果:控制台输出如下信息

    Performing system checks...

     

    System check identified no issues (0 silenced).

     

    You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.

    Run 'python manage.py migrate' to apply them.

     

    March 13, 2017 - 23:14:26

    Django version 1.10.6, using settings 'mysite.settings'

    Starting development server at http://127.0.0.1:8000/

    Quit the server with CTRL-BREAK.

     

    访问http://127.0.0.1:8000/,ok

     

     

    运行服务时,可指定端口,如下

    python manage.py runserver 8080

     

    也可以用指定ip来访问服务,放入如下

    1.运行如下命令

    python manage.py runserver 0.0.0.0:8000

     

    2.编辑project_dir/settings.py文件,把服务所在ip添加到 ALLOWED_HOSTS列表中,如下

    ALLOWED_HOSTS  = ['192.168.1.103']

     

    点击runserver查看更多关于runserver的说明

     

     

    创建投票app

    项目(project) vs 应用(app)

    应用:即一个web应用,比如web 博客系统,存放公共记录的数据库,或者一个简单的投票系统。

    项目:特定网站应用和配置的集合。一个项目包含多个应用,而一个应用只能在一个项目中。

     

    接下来,在项目根目录下,创建poll应用,这样方便作为顶级模块导入。

    例:在manage.py所在目录,即项目根目录下运行命令来创建polls app

    F:projectDjangoFirstAppmysite>python manage.py startapp polls

     

    运行后生成polls文件:

    mysite/

    polls/

        __init__.py

    admin.py

    apps.py

    migrations/

        __init__.py

        models.py

        tests.py

        views.py

     

    编写第一个view

    编辑polls/views.py文件,添加python代码(带背景色部分)如下

    from django.shortcuts import render

     

    # Create your views here.

    from django.http import HttpResponse

     

    def index(request):

        return HttpResponse("hello, world. You're at the polls index")

     

    为了调用这个index view,需要把它映射到一个URL,因此需要一个URL 配置。

    在polls目录下,新建一个名为urls.py的文件,以创建URLConf。现在app目录看起来如下:

     

    polls/

        __init__.py

        admin.py

        apps.py

        migrations/

            __init__.py

        models.py

        tests.py

        urls.py

        views.py

     

    编辑urls.py,添加如下代码

    from django.conf.urls import url

     

    from . import views

     

    urlpatterns = [

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

        ]

     

    配置项目URLConf

    编辑mysite/urls.py文件,如下

     

    from django.conf.urls import include, url

    from django.contrib import admin

     

    urlpatterns = [

        url(r'^polls/', include('polls.urls')),

        url(r'^admin/', admin.site.urls),

    ]

     

    浏览器访问

     

    说明:

    1) 正则表达式:$, xxx$:匹配xxx结尾的字符串)。

    2) 当Django遇到include()时,会先把请求中的url同include()函数对应的正则表达式匹配(例中按先后顺序分别为:'^polls/','^admin/',如果匹配到,则把URL中匹配到的字符串之后的剩余URL扔给include引用的app URLconf进行后续处理。

     

    例子:修改polls/urls.py内容如下

    from django.conf.urls import url

     

    from . import views

     

    urlpatterns = [

        url(r'test', views.index, name='index'),

        ]

     

    修改访问连接:

    http://127.0.0.1:8000/polls/testview

     

    访问结果同上。

     

    3)当且仅当需要包含其它应用的URLConf式时使用include()。这里admin.site.urls是个特例。

     

    url函数

    url函数接收4个参数:必选参数regex,view,可选参数 kwargs和name。

    参数regex: 字符串类型的正则表达式。Django会从urlpatterns list中第一个正则表达式子开始匹配查找直到找到一个匹配的。

     

    注意:正则表达匹配查找时,不搜索GET和POST参数以及域名。比如请求 https://www.example.com/myapp/, URLconf只查找myapp/,又如https://www.example.com/myapp/?page=3,URLconf只查找myapp/

     

    注:正则表达式在第一次加载URLconf模块时就进行了编译,只要不是太复杂的正则表达式,查找速度都很快。

     

    参数view:当Django找到匹配正则表达式的字符串时,会调用view函数,并把一个HttpRequest对象当作第一个函数参数,把通过正则表达式“捕获”的其它值作为其它参数。如果使用simple capture,那么捕获的值以位置参数传递,如果使用named capture则以关键词参数传递。

     

    参数kwargs:关键词参数,以字典方式传递给目标view的关键词参数。

     

    参数name:命名URL,以便在Django其它地方引用时不产生歧义。

     

    参考连接:

    https://docs.djangoproject.com/en/1.10/intro/tutorial01/

     

    第一个 Django app Part2

    建立数据库

    打开mysite/settings.py。这是个普通的python模块,拥有代表Django配置的模块级变量。

     

    默认的,配置使用SQLite。如果你对数据库不熟悉,或者仅是想使用试用Djano,这是个最容易的选择。SQLite包含在python中,所以不要安装其它任何东西来提供数据库支持。但是开始真正的项目时,可能需要使用其它更有伸缩性的数据库比如PostgreSQL。

     

    如果想使用其它数据库,安装数据库并改变DATABASE 'default'项中的关键词来匹配数据库连接,如下:

     

    DATABASES = {

        'default': {

            'ENGINE': 'django.db.backends.sqlite3',

            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

        }

    }

     

    说明:

    ENGINE:可选值是'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', 或者'django.db.backends.oracle'。其它后端也可以,查看详情

     

    NAME:数据库名字。

    如果使用SQLite,数据库文件将存放在电脑上,这种情况下,NAME应该为绝对路径,包含数据库文件的文件名。默认值如上,把数据库文件存放在项目根目录下。

     

    如果不使用SQLite,需要设置额外参数如 USER, PASSWORD,和HOST。更多详情参考DATABASES.

     

    另外,确保提供的USER具备“create database”权限。

     

     

    编辑mysite/settings.py,设置TIME_ZONE为你的时区。

     

    注意INSTALLED_APPS设置,该设置包含了Django实例中激活的所有Django应用。应用可在多个项目中使用,可以打包并发布给其它项目使用。

     

    默认的,INSTALLED_APPS包含以下来自Django应用:

    django.contrib.admin - 管理后台

    django.contrib.auth - 授权系统

    django.contrib.contenttypes - content type框架

    django.contrib.sessions - 会话框架

    django.contrib.message - 消息框架

    django.contrib.staticfiles - 管理静态文件的框架

     

    其中,一些应用使用了数据库表,所以,我们需要在使用它们之前创建数据库表。为了达到这个目的,执行以下命令:

    python manage.py migrate

    Operations to perform:

      Apply all migrations: admin, auth, contenttypes, sessions

    Running migrations:

      Applying contenttypes.0001_initial... OK

      Applying auth.0001_initial... OK

      Applying admin.0001_initial... OK

      Applying admin.0002_logentry_remove_auto_add... OK

      Applying contenttypes.0002_remove_content_type_name... OK

      Applying auth.0002_alter_permission_name_max_length... OK

      Applying auth.0003_alter_user_email_max_length... OK

      Applying auth.0004_alter_user_username_opts... OK

      Applying auth.0005_alter_user_last_login_null... OK

      Applying auth.0006_require_contenttypes_0002... OK

      Applying auth.0007_alter_validators_add_error_messages... OK

      Applying auth.0008_alter_user_username_max_length... OK

      Applying sessions.0001_initial... OK

     

    F:projectDjangoFirstAppmysite>

     

    migrate查找INSTALLED_APPS设置,然后根据mysite/setting.py中的数据库设置和应用程序的数据库迁移

    创建必要的数据库。查看创建的表:数据库客户端输入命令dt(PostgreSQL),.shema(MySQL), SELECT TABLE_NAME FROM USER_TABLES(Oracle);

     

    提醒:一些默认的应用我们不需要,可以在运行migrate之前删除、注释掉。

     

    创建模块

    将在poll应用中创建两个模块:Question和Choice。每个Question包含一个问题和发布日期,每个Choice有两个域:选项文字和投票计数器。每个Choice同一个Question关联。

     

    编辑polls/models.py文件

    from django.db import models

     

    # Create your models here.

    class Question(models.Model):

        question_text = models.CharField(max_length=200)

        pub_date = models.DateTimeField('date published')

     

     

    class Choice(models.Model):

        question = models.ForeignKey(Question, on_delete=models.CASCADE)

        choice_text = models.CharField(max_length=200)

        votes = models.IntegerField(default=0)

        

     

    每个模块都由django.db.models.Model的子类表示,类变量代表model中数据库Field。

     

    每个域由一个Field类(比如代表字符串的CharField,代表时间的DateTimeField)实例表示,告诉Django每个field可容纳什么类型的数据。

     

    Field实例(比如 question_text、pub_date)的名字,即为域的名字,可在python代码中使用,同时数据库也将把它当表字段名使用。

     

    给Field提供的第一个可选的位置参数可用来生成便于人易读的名字。如果未提供,则使用机器易读的名字作为人类易读的名字。例中,我们仅为Question.pub_date提供了人类易读的名字date published,其它模块Field实例则使用机器易读的名字,比如choice_text。

     

    一些Field需要必选参数,比如CharField,需要提供max_length。这不仅是用于数据库模式(schema),还用于合法性验证(validation)。

     

    Field还有各种可选参数,比如例中把votes的default值设置为0。

     

    最后,注意这里使用ForeignKey来确立关系,这告诉Django每个Choice和单个Question关联。Django支持所有公共数据库关系:多对一,多对多,一对一。

     

    激活模块

    上述模块代码给Django提供了许多信息,拥有它,Django可:

    1)为该app创建数据库模式(CREATE TABLE语句)

    2)为访问Question和Choice对象创建Python 数据库访问api

     

    但是,要先告诉项目已安装polls应用。

     

    为了在项目中包含该app,需要在INSTALLED_APPS设置中添加引用。PollsConfig类位于polls/apps.py文件中,点分路径为jango.apps.PollsConfig,如下:

     

    from django.apps import AppConfig

     

     

    class PollsConfig(AppConfig):

        name = 'polls'

     

    编辑mysite/settings.py,添加点分路径(带背景色内容)

    INSTALLED_APPS = [

        'polls.apps.PollsConfig',

        'django.contrib.admin',

        'django.contrib.auth',

        'django.contrib.contenttypes',

        'django.contrib.sessions',

        'django.contrib.messages',

        'django.contrib.staticfiles',

    ]

     

    运行命令:

    F:projectDjangoFirstAppmysite>python manage.py makemigrations polls

    Migrations for 'polls':

      pollsmigrations001_initial.py:

        - Create model Choice

        - Create model Question

        - Add field question to choice

     

    通过运行makemigrations,告诉django你对模块做了些改动,并且希望记录这些改动(但不立即执行这些改动),这些改动存在在磁盘文件,上例中文件为polls/migrations/0001_initial.py。可易方式读取这些改动,查看migration带来的sql执行。

     

    python manage.py sqlmigrate polls 0001

    BEGIN;

    --

    -- Create model Choice

    --

    CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "c

    hoice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);

    --

    -- Create model Question

    --

    CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,

    "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);

    --

    -- Add field question to choice

    --

    ALTER TABLE "polls_choice" RENAME TO "polls_choice__old";

    CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "c

    hoice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integ

    er NOT NULL REFERENCES "polls_question" ("id"));

    INSERT INTO "polls_choice" ("question_id", "choice_text", "votes", "id") SELECT

    NULL, "choice_text", "votes", "id" FROM "polls_choice__old";

    DROP TABLE "polls_choice__old";

    CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");

    COMMIT;

     

     

    注意:

    1)Django会自动添加主键 id(可重写)

    2)约定的,Django会添加”_id”到外键域(可重写)

     

    可执行python manage.py check,在不执行迁移或改动数据库的情况下,来检查项目中的问题

     

    接着,执行migrate在数据库中创建模块表,即让上述存储的改动在应用中生效。

    F:projectDjangoFirstAppmysite>python manage.py migrate

    Operations to perform:

      Apply all migrations: admin, auth, contenttypes, polls, sessions

    Running migrations:

      Applying polls.0001_initial... OK

     

    阅读django-admin documentation查看manage.py工具的更多功能。

     

    API交互

    调用python shell

    python manage.py shell

    Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AM

    D64)] on win32

    Type "help", "copyright", "credits" or "license" for more information.

    (InteractiveConsole)

     

    同直接运行python不一样,因为manage.py设置DJANGO_SETTINGS_MODULE环境变量,为mysite/settings.py文件提供python导入路径。

     

    注:也可以不用manage.py,直接设置DJANGO_SETTINGS_MODULE环境变量,然后运行python并设置Django

    set DJANGO_SETTINGS_MODULE=mysite.settings

     

    python

    Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AM

    D64)] on win32

    Type "help", "copyright", "credits" or "license" for more information.

    >>> import django

    >>> django.setup()

     

    按这种方式,必须在manage.py所在目录下开启python。或者确保这个目录在python的path中,这样import mystie才起作用。

     

    调用数据库api

    >>> from polls.models import Question, Choice

     

    # 系统中还没有问题

    >>> Question.objects.all()

    <QuerySet []>

     

    # 创建一个新的Question

    >>> from django.utils import timezone

    >>> q = Question(question_text="what's up?", pub_date=timezone.now())

     

    # 保存对象到数据库。

    >>> q.save()

     

    # 输出也可能是1L,而不是1,取决于数据库。

    >>> q.id

    1

     

    # 通过python属性访问模块field

    >>> q.question_text

    "what's up?"

    >>> q.pub_date

    datetime.datetime(2017, 3, 22, 12, 57, 18, 103269, tzinfo=<UTC>)

     

    # 通过修改属性来修改field

    >>> q.question_text = "what's up?"

    >>> q.save()

     

    # objects.all()展示数据库中的所有问题。

    >>> Question.objects.all()

    <QuerySet [<Question: Question object>]>

     

    为了更清楚点的显示对象,可编辑polls/models.py文件,添加一个__str__()方法到Question和Choice。

    from django.db import models

    from django.utils.encoding import python_2_unicode_compatible

     

    @python_2_unicode_compatible  # 如果需要支持python2

    Question(models.Model):

        # ...

        def __str__(self):

            return self.question_text

     

    @python_2_unicode_compatible # 如果需要支持python2

        # ...

        def __str__(self):

            return self.choice_text

     

     

    添加自定义方法

    import datetime

    from django.db import models

    from django.utils import timezone

     

    class Question(models.Model):

        # ...

        def was_published_recently(self):

            return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

     

     

    再次运行python manage.py shell

    >>> from polls.models import Question, Choice

    # 确认 __str__() 是否起作用

     

    >>> Question.objects.all()

    <QuerySet [<Question: What's up?>]>

     

    # Django提供了一个完全由关键词参数驱动的丰富的数据库API。

    >>> Question.objects.filter(id=1)

    <QuerySet [<Question: What's up?>]

     

    >>>> Question.objects.filter(question_text__startswith='What')

    <QuerySet [<Question: What's up?>]>

     

    # 获取今年发布的问题

    >>> from django.utils import timezone

    >>> current_year = timezone.now().year

    >>> Question.objects.get(pub_date__year=current_year) #注意 pub_date和year中间有两下划线

    <Question: What's up?>

     

    # 如果请求的id不存在,将抛出异常.

    >>> Question.objects.get(id=2)

    Traceback (most recent call last):

    ...DoesNotExist: Question matching query does not exist.

     

    # 按主键查询,以下命令等同于Question.objects.get(id=1)

    >>> Question.objects.get(pk=1)

    <Question: What's up?>

     

    # 确认自定义方法起作用

    >>> q = Question.objects.get(pk=1)

    >>> q.was_published_recently()

    True

     

    # 给Question多个Choice。调用create函数构造一个新的Choice对象,执行INSERT 语句,添加choice到#获取的choice set,然后返回新建的Choice对象。Django创建了一个集合以容纳ForeignKey 关系的另一方#(如 question’s choice)。

    >>> q = Question.objects.get(pk=1)

     

    # 展示相关对象集的choice - 目前为空

    >>> q.choice_set.all()

    <QuerySet []>

     

    >>> q.choice_set.create(choice_text='Not much', votes=0)

    <Choice: Not much>

    >>> q.choice_set.create(choice_text='The sky', votes=0)

    <Choice: The sky>

    >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

     

    # Choice 对象有API可访问与其相关的Question对象。

    >>> c.question

    <Question: What's up?>

     

    # 反之,Question 对象可访问Choice对象。

    >>> q.choice_set.all()

    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

    >>> q.choice_set.count()

    3

     

    # 只要你需要,API自动跟随关系。关系之间用下划线关联。

    # 找出同Choice关联的question,要求qub_date在今年以内

    >>> Choice.objects.filter(question__pub_date__year=current_year)

    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

     

    # 删除其中一个choice

    >>> c = q.choice_set.filter(choice_text__startswith='Just hacking')

    >>> c.delete()

     

    查看更多关于对象关系:Accessing related objects,更多关于使用双下划线进行域查找:Field lookups,数据库API完整信息:Database API reference

     

     

    介绍Djando Admin

    创建管理员用户

    python manage.py createsuperuser

    Username (leave blank to use 'laiyu'): admin

    Email address: 1033553122@qq.com

    Password:

    Password (again):

    This password is too short. It must contain at least 8 characters.

    Password:

    Password (again):

    Superuser created successfully.

     

    开启开发服务器

    Django管理员站点默认是激活的。

     

    如果服务器未运行,执行如下命令

    python manage.py runserver

     

    浏览器访问

     

     

    进入站点

    输入帐号,密码登录

     

    可看到groups和users,这是由django.contrib.auth提供的,django的认证框架。

     

    使得poll应用在站点管理页中可修改

    如上,没看到poll应用。要展示该页面,还需告诉admin,Question对象拥有admin接口。为了达到这个目的,打开polls/admin.py,按如下编辑

    from django.contrib import admin

     

    # Register your models here.

    from .models import Question

     

    admin.site.register(Question)

    点击Questions

     

     

    点击what’s up

     

     

     

    第一个 Django app Part3

    Django中,web页面和其它内容都是从views派生的,每个view由python函数(或方法)表示,Django通过检查请求的域名后面的那部分URL来选择view。

     

    编写更多的视图(view)

    在polls/view.py中添加几个视图

     

    def detail(request, question_id):

        return HttpResponse("You're looking at question %s." % question_id)

     

    def results(request, question_id):

    response = "You're looking at the results of question %s."

    return HttpResponse(response % question_id)

     

    def vote(request, question_id):

        return HttpResponse("You're voting on question %s." % question_id)

     

     

    然后在polls/urls.py中添加url()调用

    polls/urls.py

     

    from django.conf.urls import url

     

    from . import views

     

    urlpatterns = [

        # ex: /polls/

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

        # ex: /polls/3/

        url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),

        # ex: /polls/3/results/

        url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),

        # ex: /polls/3/vote/

        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),

    ]

     

    浏览器访问

     

     

    默认的,从站点请求页面,比如“/polls/3”,Django会先加载mysite.urls python模块,因为ROOT_URLCONF配置指向它。先查找urlpatterns变量,并按顺序解析正则表达式,如果找到匹配‘^polls/’的,把URL中匹配到的字符串polls/去掉,然后把后面剩余部分“3/”扔给polls.urls URLCONf进行后续处理。接着匹配到r'^(?P<question_id>[0-9]+)/$',调用detail view,如下:

     

    detail(request=<HttpRequest object>, question_id=3)

     

    question_id=3 来自(?P<question_id>[0-9]+)。使用双括号于正则表达式,可捕获正则表达式匹配到的文本,然后当作参数发给view函数。?P<question_id>定义了用于匹配正则表达式的名称,即用来匹配函数关键词参数的pattern,[0-9]+用于匹配数字序列。

     

    编写执行实际任务的视图

    每个视图都负责这两件事之一:返回一个包含请求页面内容的HttpResponse()对象,或者是抛出异常,比如Http404

     

    视图可从数据库读取记录,也可使用Django的模板系统,或者是第三方的Python模板系统,可生成PDF文件,输出XML,创建ZIP及其它你想要的。

     

    根据发布日期,展示最新的5个question,逗号分隔。

    在polls/view.py中添加以下内容,其它保持不变

     

    from .models import Question

     

    # Create your views here

    def index(request):

         latest_question_list = Question.objects.order_by('-pub_date')[:5]

         output = ','.join([q.question_text for q in latest_question_list]) 

         return HttpResponse("Hello, world. You’re at the polls index")

     

    这里有个问题,就是视图中的页面设计是写死的,如果想改变页面样式,需要编辑Python代码。这里,使用Django的模板系统来创建一个可用视图。

     

    先在polls目录下创建一个名为templates的目录,Django会在这里查找目标。

     

    项目的TEMPLATES设置描述了Django将咋样加载并渲染模板。默认的,配置文件配置了一个DjangoTemplates后端,其APP_DIRS选项被设置为True。约定的,DjangoTemplates会在每个INSTALLED_APP中查找templates子目录。

     

    在刚创建的templates目录下创建另一个polls目录,并在该目录下新建index.html文件。换句话说,template应该在polls/templates/polls/index.html。由于app_directories模板加载器按上述描述的方式工作,所以,可简单的使用polls/index.html引用该模板。

     

     

    注意:模板命名

    我们可直接在polls/templates目录下存放我们的模板,但是这样不好,Django会选择它查找到的第一个名字匹配的模板,这样的话,如果你在另一个不同的应用下有相同名称的目标,Django无法区分它们。所以,我们需要对它们进行命名,也就是把那些目标存放在以应用自身命名的另一个目录。

     

    编辑模板

    {% if latest_question_list %}

    <url>

        {% for question in latest_question_list %}

            <li><a href="/polls/{{ question.id }}" > {{ question.question_text }}</a></li>

        {% endfor %}

    </url>

    {% else %}

         <p>No polls are available.</p>

    <% endif %>

     

    使用模板来更新polls/views.py里面的index视图。

    from django.http import HttpResponse

    from django.template import loader

     

    from .models import Question

     

    def index(request):

    latest_question_list = Question.objects.order_by('-pub_date')[:5]

    template = loader.get_template('polls/index.html')

    context = {

        'latest_question_list':latest_question_list

    }

    return HttpResponse(template.render(context, request))

     

    代码加载名为polls/index.html的模板,并传递给context。context为一个字典,映射模板变量到python对象。

     

    浏览器访问

     

    点击连接,打开详情。

     

    捷径:render()

    编辑polls/views.py

     

    from django.shortcuts import render

     

    from .models import Question

     

     

    def index(request):

        latest_question_list = Question.objects.order_by('-pub_date')[:5]

        context = {'latest_question_list': latest_question_list}

        return render(request, 'polls/index.html', context)

     

    render函数接收一个request作为其第一个参数,模板名字作为第二个参数,字典作为可选的第三个参数。函数返回一个经过给定context渲染的HttpResponse对象。

     

    抛出404错误

    polls/views.py

    from django.http import Http404

    from django.shortcuts import render

    from .models import Question

     

    # ...

    def detail(request, question_id):

        try:

            question = Question.objects.get(pk=question_id)

        except Question.DoesNotExist:

            raise Http404("Question does not exist")

        return render(request, 'polls/detail.html', {'question': question})

     

    新建模板

    polls/templates/polls/detail.html

    {{ question }}

     

    运行浏览器

     

     

     

    捷径:get_object_or_404()

    推荐使用get_object_or_404()

     

    使用模板系统

    到回来看detail视图,针对给定变量question,polls/detail.html模板可能如下

    <h1>{{ question.question_text }}</h1>

    <ul>

    {% for choice in question.choice_set.all %}

        <li>{{ choice.choice_text }}</li>

    {% endfor %}

    </ul>

     

    运行结果如下

     

     

    模板系统使用点查找(dot-lookup)语法访问变量属性。{{ question.question_text }}为例,先在question对象上执行字典查找,然后在视图属性中查找-这种情况下,找到了。如果属性查找失败,则尝试列表索引查找。

     

    方法调用发生咋{ % for %}循环:question.choice_set.all()被转为python代码 question.choice_set.all(),返回适合{% for %}标签,由Choice对象组成的可迭代对象。

     

    移除模板中的写死的URL

    polls/index.html中编写的指向question的连接

    <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

     

    写死的url,对有许多模板的项目来说,更改url会变成一件很困难的事情。由于polls.urls模块的url()函数中定义了命名的参数,可通过{% url %}模板标签来移除在url配置中,特定url路径上定义的依赖:

     

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

     

    以下是'detail' polls/urls.py中的定义

    ...

    # 'name'的值被 {% url %} 模板标签定义

    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),

    ...

     

    这样当需要更改应用的url,比如更改为polls/specifics/12/,可以不用在目标中更改写死的url,直接在polls/urls.py中更改。

    ...

    # 添加 'specifics'到url

    url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),

    ...

     

    给URL名字增加名称空间

    在URLConf中添加名称空间,以便使用{% url %}模板标签时,django能区分不用应用的url。

     

    在polls/urls.py中添加app_name来设置应用的名称空间。

    from django.conf.urls import url

     

    from . import views

     

    app_name = 'polls'

    urlpatterns = [

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

        url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),

        url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),

        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),

    ]

     

    更改polls/index.html模板

    更改模板

    polls/templates/polls/index.html

     

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

     

    为如下:

    polls/templates/polls/index.html

    <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

     

    第一个 Django app Part4

    编写一个简单的表格

    更新detail模板(polls/detail.html)

    <h1>{{ question.question_text }}</h1>

     

    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

     

    <form action="{% url 'polls:vote' question.id %}" method="post">

    {% csrf_token %}

    {% for choice in question.choice_set.all %}

        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />

        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />

    {% endfor %}

    <input type="submit" value="Vote" />

    </form>

     

    说明:

    1)每个choice都有一个对应的radio按钮。每个radio按钮的值都同关联问题choice id关联。每个radio按钮的名字为choice。这也就意味着,当某人选择其中一个radio按钮并提交表单时,发送POST数据choice=#,其中#表示所选择的choice的id

     

    2)设置表单的action为 {% url 'polls:vote' question.id %},设置method='post'(对立的method='get'),这很重要,因为这会改变服务器端的数据。

     

    3)forloop.counter一个表示当前循环的执行次数的整数计数器。 这个计数器是从1开始的,所以在第一次循环时 forloop.counter 将会被设置为1

     

    4)因为是POST表单,需要考虑跨站脚本攻击,所以使用{% csrf_token %}模板标签。

     

    接着,创建处理提交数据的视图

    还记得polls/urls.py有如下设置:

    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),

     

    修改polls/views.py中的样本函数vote

    polls/views.py

     

    from django.shortcuts import get_object_or_404, render

    from django.http import HttpResponseRedirect, HttpResponse

    from django.urls import reverse

     

    from .models import Choice, Question

    # ...

    def vote(request, question_id):

        question = get_object_or_404(Question, pk=question_id)

        try:

            selected_choice = question.choice_set.get(pk=request.POST['choice'])

        except (KeyError, Choice.DoesNotExist):

            # Redisplay the question voting form.

            return render(request, 'polls/detail.html', {

                'question': question,

                'error_message': "You didn't select a choice.",

            })

        else:

            selected_choice.votes += 1

            selected_choice.save()

            # Always return an HttpResponseRedirect after successfully dealing

            # with POST data. This prevents data from being posted twice if a

            # user hits the Back button.

            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

     

     

    说明:

    Request.POST类似字典的对象,允许通过key名称来访问提交的数据。例中,request.POST['choice']返回字符串表示choice的ID。Request.POST值总是字符串。

     

    类似的,django提供了request.GET来访问GET data

     

    如果POST数据中无choice,Request.POST['choice']将抛出KeyError。

     

    增加choice计数后,code返回HttpResponseRedirect而非正常的HttpResponse。HttpResponseRedirect携带单个参数:将要重定向至的url。

     

    使用reverse()函数避免在view视图中写死url。reverse()调用返回一个类似如下的字符串:

    '/polls/3/results'

     

    其中,3为问题id,该重订向url将会调用'results'视图来展示最终页面。

     

    As mentioned in Tutorial 3, request is an HttpRequest object. For more on HttpRequest objects, see the request and response documentation.

     

    投票之后,vote视图,重定向到问题的结果页面。重写vote视图:

    polls/views.py

    from django.shortcuts import get_object_or_404, render

     

    def results(request, question_id):

        question = get_object_or_404(Question, pk=question_id)

        return render(request, 'polls/results.html', {'question': question})

     

     

    创建polls/results.html模板

    polls/templates/polls/results.html

    <h1>{{ question.question_text }}</h1>

     

    <ul>{% for choice in question.choice_set.all %}

        <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>{% endfor %}</ul>

    <a href="{% url 'polls:detail' question.id %}">Vote again?</a>

     

    浏览器访问

     

     

    使用通用视图

    使用通用视图来转换poll应用。

    1)转换URLConf

    2)删除旧的,不必要的视图

    3)引入基于Django的通用视图(generic view)

     

    改良的URLConf

    polls/urls.py

     

    from django.conf.urls import url

     

    from . import views

     

    app_name = 'polls'

    urlpatterns = [

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

        url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),

        url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),

        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),

    ]

     

    改良的视图

    polls/views.py

     

    from django.shortcuts import get_object_or_404, render

    from django.http import HttpResponseRedirect

    from django.urls import reverse

    from django.views import generic

     

    from .models import Choice, Question

     

     

    class IndexView(generic.ListView):

        template_name = 'polls/index.html'

        context_object_name = 'latest_question_list'

     

        def get_queryset(self):

            """Return the last five published questions."""

            return Question.objects.order_by('-pub_date')[:5]

     

     

    class DetailView(generic.DetailView):

        model = Question

        template_name = 'polls/detail.html'

     

     

    class ResultsView(generic.DetailView):

        model = Question

        template_name = 'polls/results.html'

     

     

    def vote(request, question_id):

        ... # same as above, no changes needed.

     

     

    问题:问题列表这么调整后变成了空白,怎么解决?

     

    这里使用了两种视图:ListView和DetailView。这两种对象分别抽象了list对象的展示和特定读写的详细页面展示。

     

    每种通用视图使用model属性来区分需要作用的模块。

     

    DetailView视图期望从ULR捕获的主键值被称为pk,所以把question_id改成了pk

     

    默认的DetailView视图使用名为<app name>/<model name>_detail.html的模板。例子中,使用polls/question_detail.html。template_name属性告诉Django使用指定名称的模板,而不是使用默认模板名称。

     

    类似的,ListView使用<app name>/<model name>_list.html模板。

     

    对于ListView,自动生成context变量question_list。为了重写这个,提供context_object_name来指定自己的变量latest_question_list。

     

     

    第一个 Django app Part5

     

    第一个 Django app Part6

    自定义app样式和感观。

     

    在polls目录下新建static。Django会在这查找静态文件。类似查找模板。

    Django的STATICFILES_FINDERS设置包含了finder list,告诉它怎么查找静态文件。其中一个默认的finder AppDirectoriesFinder会在每个INSTALLED_APPS查找static子目录。管理站点对静态文件使用相同的目录结构

     

    static目录下新建一个polls目录,在该目录下新建名为style.css的文件。使用polls/style.css引用资源

     

    编辑style.css

    polls/static/polls/style.css

     

    li a {

        color: green;

    }

     

    修改polls/templates/polls/index.html

    {% load static %}

     

    <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />

     

    运行效果:

     

     

    {% static %}模板标签生成静态文件的绝对URL。

     

    添加背景图片

    在polls/static/polls目录下新建images目录,并在该目录下存放一张名为background.gif的图片。

     

    修改sytle.cass,新增代码如下

    body {

        background: white url("images/background.gif") no-repeat right bottom;

    }

     

    刷新页面,可看到屏幕右上方显示动态图片

     

    注意:{% static %}模板标签不适用非Django生成的静态文件,比如样式表单。

     

     

    第一个 Django app Part7

    自定义管理站点 form

    polls/admin.py

     

    from django.contrib import admin

     

    from .models import Question

     

     

    class QuestionAdmin(admin.ModelAdmin):

        fields = ['pub_date', 'question_text']

     

    admin.site.register(Question, QuestionAdmin)

     

    上述代码使得Question field位于Publication date之后

     

     

     

    分隔成多个fieldsets

     

    from django.contrib import admin

     

    from .models import Question

     

     

    class QuestionAdmin(admin.ModelAdmin):

        fieldsets = [

            (None,               {'fields': ['question_text']}),

            ('Date information', {'fields': ['pub_date']}),

    ]

     

    admin.site.register(Question, QuestionAdmin)

     

     

    添加关联对象

    方式1,注册模块

    polls/admin.py

     

    from django.contrib import admin

     

    from .models import Choice, Question

    # ...

    admin.site.register(Choice)

     

     

     

    那个form中,Question field是一个select box,包含数据库中的每个问题。Django知道ForeignKey应该在<select> box中出现。

     

    Question的编辑和+号按钮,可分别打开question编辑(需要先选定问题才可用)和添加页面。

     

    方式2:

    from django.contrib import admin

     

    from .models import Choice, Question

     

     

    class ChoiceInline(admin.StackedInline):

        model = Choice

        extra = 3

     

     

    class QuestionAdmin(admin.ModelAdmin):

        fieldsets = [

            (None,               {'fields': ['question_text']}),

            ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),

        ]

        inlines = [ChoiceInline]

     

    admin.site.register(Question, QuestionAdmin)

     

     

    默认3个choice是由extra指定的,点击Add another Choice链接,自动新增一个Choice。

     

    这里有个问题,就是占用空间比较大。为此,Django提供了一个tabular的方式来展示inline相关对象。

    polls/admin.py

     

    class ChoiceInline(admin.TabularInline):

        #...

     

     

     

    delete?列用于提供删除操作(通过点击Add another Choice增加的行。)

     

    自定义admin change list

    展示单个field。使用list_display admin选项,供展示的field元组,比如列。

     

    class QuestionAdmin(admin.ModelAdmin):

        # ...

        list_display = ('question_text', 'pub_date')

     

    还可添加was_published_recently()方法。

     

    class QuestionAdmin(admin.ModelAdmin):

        # ...

        list_display = ('question_text', 'pub_date', 'was_published_recently')

     

     

     

    可点击列标题来排序,was_published_recently列除外,因为不支持按任意方法的输出排序。另外,was_published_recently列默认为方法名称(下划线替换了空格)。

     

    通过给方法增加属性来改善。

    polls/models.py

     

    class Question(models.Model):

        # ...

        def was_published_recently(self):

            now = timezone.now()

            return now - datetime.timedelta(days=1) <= self.pub_date <= now

        was_published_recently.admin_order_field = 'pub_date'

        was_published_recently.boolean = True

        was_published_recently.short_description = 'Published recently?'

     

    效果:

     

     

    增加过滤器

    修改polls/admin.py,新增list_filer,新增代码如下。

     

    list_filter = ['pub_date']

     

    效果如下:

     

    filter展示类型取决于你过滤的field。因为pub_date是DateTimeField,Django知道怎么给恰当的filter选项:Any date,Today等

     

    增加搜索

    search_fields = ['question_text']

     

    效果如下:

    自定义admin样式和感观

    自定义项目模板

    在项目目录中(包含manage.py文件)下创建template目录。template可放在Django可访问的任何文件系统,但是保持模板在项目里,是需要好好遵守的约定。

     

    编辑mysite/settings.py,在TEMPLATES设置中添加一个DIRS选项。

    mysite/settings.py

     

    TEMPLATES = [

        {

            'BACKEND': 'django.template.backends.django.DjangoTemplates',

            'DIRS': [os.path.join(BASE_DIR, 'templates')],

            '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',

                ],

            },

        },

    ]

     

    DIRS是当加载Django模板时,需要检查的文件系统目录列表,是一个搜索路径。

     

    现在在templates目录中新建一个名为admin的目录,从默认的Django admin模板目录(django/contrib/admin/templates)中拷贝模板文件admin/base_site.html到该目录。

     

    编辑文件,替换{{ site_header|default:_('Django administration') }}为自己的站点名称。

    {% block branding %}

    <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>

    {% endblock %}

     

    该例字告诉我们使用这种方法来重写模板。

     

    模板包含很多类似{% block branding %} and {{ title }}的文本,{%和{{标签是Django的模板语言。

     

    参考连接:

    https://docs.djangoproject.com/en/1.10/intro/tutorial02/

  • 相关阅读:
    在spring MVC的controller中获取ServletConfig
    支付宝移动接入报系统繁忙,參数错误等错误
    Android:控件WebView显示网页
    android WebView总结
    关于用WebView或手机浏览器打开连接问题
    Android解决Fragment多层嵌套时onActivityResult无法正确回调的问题
    Fragment嵌套Fragment时遇到的那些坑
    Android 多个Fragment嵌套导致的三大BUG
    Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理
    Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复
  • 原文地址:https://www.cnblogs.com/shouke/p/10157590.html
Copyright © 2011-2022 走看看