django应用2,3,4

1.(接着django应用1继续 。

数据库配置

1.1编辑 mysite/settings.py 文件前,先设置 TIME_ZONE 为你自己时区。

INSTALLED_APPS 默认设置django自带应用:这些应用被默认启用是为了给常规项目提供方便。

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

这个 migrate 命令检查 INSTALLED_APPS 设置,

python manage.py migrate

2.创建模型

2.1 编辑 polls/models.py 文件:

from django.db import models


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)

解释:

(1)每个模型被表示为django.db.models.Model 类的子类。每个模型有一些类变量,它们都表示模型里的一个数据库字段。
(2)注意在最后,我们使用 ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。Django 支持所有常用的数据库关系:多对一、多对多和一对一。

3.激活模型

3.1在文件 mysite/settings.py 中 INSTALLED_APPS 子项添加

’polls.apps.PollsConfig’,
这个django项目中包含polls应用,接着运行
以下命令:

 python manage.py makemigrations polls

通过运行 makemigrations 命令,Django 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移。模型的迁移数据,它被储存在 polls/migrations/0001_initial.py 里。

3.2 Django 有一个自动执行数据库迁移并同步管理你的数据库结构的命令 - 这个命令是 migrate,

python manage.py sqlmigrate polls 0001

3.3 再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:

python manage.py migrate

这个 migrate 命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。
改变模型需要这三步:

(1)编辑 models.py 文件,改变模型。
(2)运行 python manage.py makemigrations 为模型的改变生成迁移文件。
(3)运行 python manage.py migrate 来应用数据库迁移。

4 初试API

进入交互式 Python 命令行,通过以下命令打开 Python 命令行:

python manage.py shell

5. orm 操作总结

  1. 查询表中所有数据
    Question.objects.all() 返回model对应
  2. 插入数据 默认自动提交。
    q = Question()
    q.save()
  3. 带条件查询
    类.objects.filter(列=值,列2=值2)
  4. 模糊匹配 字段后跟表示特殊操作__开头
    类.objects.filter(字段__startswith=‘匹配子串’)
  5. 查询除了filter,也可以get,功能一致
    类.objects.get(列=值)
    get查询不到报异常。(推荐)filter查询不到返回空结果集。
  6. 类实例相当于表中一行数据,可以调用实例方法
    q = Question.objects.get(id=1)
    q.方法()
  7. 反向查询
    需求,查询question表中“下周五考试吗”这个问题对应的选项,如果是sql,要先select id from question表 where text=“下周”得到qid,再select * from choice where question_id=qid;
    而orm带我们只需要q.关联表_set.all()。
    优点:省一条查询代码。缺点:不太好理解
  8. 一对多关系
    先把一的方面的一行数据get出来,
    q = Question.objects

django应用3 介绍

介绍

有了前面的学习基础之后,我迫不及待的想要做出漂亮的界面了。把前端页面从后端返回到路由中,这就是视图函数所要做的事。

在我们的投票应用中,我们需要下列几个视图:

  • 问题索引页——展示最近的几个投票问题
  • 问题详情页——展示某个投票的问题和不带结果的选项列表。
  • 问题结果页——展示某个投票的结果。
  • 投票处理器——用于响应用户为某个问题的待定选项投票的操作

在Django中,网页和其它内容都是从视图派生而来。每一个视图表现为一个简单的Python函数或者方法。Django将会根据用户请求的URL来选择使用哪个视图(更准确的说,是根据URL中域名之后的部分)。

URL有两种方式来实现分页,一种是URL部分可变,一种是URL传参的方式(问号传参)

编写视图函数

现在向polls/views.py里添加更多视图。这些视图有一些不同,因为他们接受参数:

polls/views.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模块里,只需要添加几个url()函数调用就行:

from django.urls import path

from .import views

urlpatterns = [
    # http://ip:port/polls/
    path('', views.index, name='index'),
    # 首页问题列表 /polls/index/
    path('index/', views.index, name='index'),
    # 问题详情页 ex:/polls/1/
    path('<int:question_id>/', views.detail, name='detail'),
    # 投票结果页
    path('<int:question_id>/results/', views.results, name='results'),
    # 去投票,选项计数加一/polls/5/vote
    path('<int:question_id>/vote/', views.vote, name='vote'),

]

然后在浏览器中输入相应的路由即可查看这些视图函数的返回值。

写一个真正有用的视图

上面的视图函数虽然能被正确访问,但是并没什么用,只是测试而已,现在,照着官方文档编写一个真正有用的视图把

每个视图函数必须要做的只有两件事:返回一个包含被请求页面内容的HttpResponse对象,或者抛出一个异常,比如Http404。至于其它的看你的选择了。

你的视图可以从数据库里读取记录,可以使用一个模板引擎,可以生成一个PDF文件,可以输出一个XML,创建一个ZIP文件,你可以做任何你想做的是,使用任何你想用的python库。

Django只要求返回的是一个HttpResponse,或者抛出一个异常。

因为Django自带的数据库API很方便,所以我们可以在视图函数里使用它。我们在index()函数里插入了一些新内容,让它能展示数据库里以发布日期排序的最近5个投票问题,以空格分割:

polls/views.py
from django.http import HttpResponse
from .models import Question


def index(request):
	latest_quest_list = Question.objects.order_by('-pub_date0')[:5]
	output = ', '.join([q.question_text for q in lastest_question_list])
	return HttpResponse(output)
# 这里使用了列表生成式,先迭代出q,然后访问q的question_属性
# 然后使用字符串的.join方法把列表生成式中迭代出来的项用, 分割组合成一个新字符串。

现在,这个视图函数就会返回最近的问题啦。但是还有问题,页面的设计写死在视图函数的代码。如果你想改变页面的样子,你需要编辑python代码。所以让我们通过使用Django的魔板系统,只要创建一个视图,就可以将页面的设计从代码中分离出来。

首先,在你的polls目录里创建一个templates目录。Django将会在这个目录里查找模板文件。

你项目的TEMPLATES配置项描述了Django如何载入和渲染末班。默认的设置文件设置了DjangoTemplates后端,并将APP_DIRS设置成了True。这一选项将会让DjangoTemplates在每个INSTALLED_APPS文件夹中寻找templates子目录。

在你刚刚创建的templates目录里,再创建一个目录polls,然后再其中新建一个文件index.html。换句话说,你的模板文件的路径应该是polls/templates/polls/index.html。因为Django会寻找到对应的app_directories,所以你只需要使用polls/index.html就可以引用到这一模板了。

将下面的代码输入到刚刚创建的模板文件中(index.html):

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% 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。这个上下文是一个字典,他将模板内的变量映射为python对象。

现在访问/polls/,你将会看见一个无需列表,链接指向这个投票的详情页。

一个快捷函数: render()

载入模板,填充上下文,再返回由它生成的HttpResponse对象是一个非常常用的操作流程。于是Django提供了一个快捷函数,我们用它来重写index()视图:

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)

抛出404错误

现在我们来处理投票详情视图–它会显示投票的问题标题:

polls/views.py
def detail(request, question_id):
    """
    显示一个问题的详细信息,问题内容、问题发布时间、选项内容、每个选项投票数。
    """
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question不存在")
    return render(request, 'polls/detail.html', {'question': question})

在detail.html里先写入:

{{ question }}

一个快捷函数:get_object_or_404()

尝试用get()函数获取一个对象,如果不存在就跑出Http404错误也是一个普遍的流程。Django也提供了一个快捷函数,下面是修改后的详情detail()视图代码:

polls/views.py
from django.shortcuts import get_object_or_404, render
from .models import Question

def detail(request, question_id)
	question = get_object_or_404(Question, pk=question_id)
	return render(request, 'polls/detail.html', {'question': question})

get_object_or_404()函数将Django模型作为其第一个参数和任意数量的关键字参数,并将这些参数传递给模型管理器的get()函数。如果对象不存在,它将引发Http404。

使用模板系统

detail()视图向模板传递了上下文变量question。他是一个Question对象,下面我们来改进detail.html

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice }}</li>
{% endfor %}
</ul>

模板系统统一使用点符号来访问变量的属性。在实例quesion.question_text 中,首先Django尝试对question对象使用字典查找(也就是objects.get(str)操作),如果失败了就尝试属性查找(也就是obj.str操作),结果是成功了。如果这一操作也失败的话,将会尝试列表查找(obj[int]操作)。

在% for %循环中发生的函数调用:question.choice_set.all被解释为python代码question.choice_set.all(),将会返回一个可迭代的Choice对象,这一对象在for标签内部使用。

去除模板中的硬编码URL

我们在polls/index.html里编写投票连接时,链接是硬编码的:

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

问题在于,硬编码和强耦合的链接,对于一个包含很对应用的项目来说,修改起来是十分困难的。然而,因为你在polls.urls的url()函数中通过name参数为URL定义了名字,你可以使用% url %标签代替它:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
# 通过url的name指向url,像上面一样传入一个qeustion_id来构造url,构造每个问题的详情页url

这个标签的工作方式是在polls.urls模块的URL定义中寻具有指定名字的条目。detail的url是这样定义的:

path('<int:question_id>/', views.detail, name='detail'),
...

将链接修改成这样的好处是:如果你想改变投票详情页视图的URL,比如像改成polls/xxx/1/,你不用在模板里修改任何东西(包括其他模板),只要在polls/urls.py里稍微修改一下就行:

path('xxx/<int:qeustion_id>', view.detail, name='detail'),
...

为URL名称添加命名空间

官方文档教程项目只有一个应用:polls。但是在一个真是的Django项目中,可能会有五个、十个、二十个,甚至是更多应用。Django如果分辨重名的URL呢?举个例子,polls应用里有detail视图,可能另一个博客应用里也有同名的视图。Django如何知道%url%标签到底对应那个一个应用的URL呢?

答案是:在根URLconf中添加命名空间。在polls/urls.py文件中稍作修改,加上app_name设置命名空间:

polls/urls.py
from django.urls import path

from .import views

app_name = 'polls'
urlpatterns = [
    # http://ip:port/polls/
    path('', views.index, name='index'),
	...
# 在这个文件中,设置app_name

然后,编辑polls/index.html文件,修改为指向具有命名空间的详细视图:

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

小结

视图函数的作用就是从后端返回一些信息到前端页面,然后编写相应的url路由引入这些视图。

在polls/views.py中编写视图,在polls/urls.py中编写路由

  1. 为了方便HttpResponse对象使用模板,Django提供了一个快捷函数render()render函数有三个参数,第一个参数为request,第二个参数为要引用的模板,第三个参数为传入的上下文对象context字典。注意,在Django中写视图函数,默认都要传入一个reqeust参数,否则不能正常工作。

  2. 在处理请求时,经常会抛出404错误,在这个应用中,如果问题ID所对应的问题不存在,就需要抛出一个http404异常。起先,我们在detail视图里使用try except来抛出http404异常,但是这样写太麻烦了。Django提供了一个快捷函数get_object_or_404()。get_object_or_404()函数将Django模型作为其第一个参数和任意数量的关键字参数,并将这些参数传递给模型管理器的get()函数。如果对象不存在,它将引发Http404。

  3. 在一个完整的Django项目中,可能会有很多应用。每个应用都包含很多URL,难免会出现重复的URL,为了区分它们,可以每个应用的urls中加上app_name设置命名空间,然后在别处引入URL时(比如在html页面中指向url,)在路由url前加上命名空间即可。


django应用4 介绍

本文从Django3结尾的地方讲起,我们将继续编写投票应用,专注于简单的表单处理并且精简我们的代码。

表单处理

编写一个简单的表单

让我们更新一下在上一个教程中编写的投票详细页面的模板(polls/detail.html),让它包含一个HTML(from)元素:

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

说明:

  • 上面的模板在Question的每个Choice前添加一个单选按钮。每个单选按钮的value属性是对应的各个Choice的ID。每个单选按钮的name是choice。这意味着,当有人选择一个按钮并提交表单请求时,它将发送一个POST数据choice=#,其中#为选择的choice的ID。这是HTML表单的基本概念。
  • 我们设置表单的action为(% url ‘polls:vote’ question.id %)并设置method为POST。使用method="POST"是非常重要的,因为这个提交表单的行为会改变服务器端的数据。无论何时,当你需要创建一个改变服务器端数据的表单时,请使用method="POST"。这不是Django的特定技巧;这是优秀的网站开发技巧。
  • forloop.counter指示for标签已经循环多少次。
  • 由于我们创建一个POST表单(它具有修改数据的作用),所以我们需要小心跨站点请求伪造。你不必太过担心,因为Django已经拥有一个用来防御csrf的非常容易使用的系统。简而言之,所有针对内部URL的POST表单都应该使用(% csrf_token %)模板标签。

现在,让我们来创建一个Django视图来处理提交的数据。在前一节中,我们为投票应用创建了一个URLconf:

polls/urls.py
path('<int:question_id>/vote/', views.vote, name='vote')

我们还创建了一个vote()的视图函数。现在让我们来改进这个视图函数:

polls/views
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):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': "没有这个选项"})
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

以上代码有些内容还未在本教程中提到过:

  • request.POST是一个类字典独享,让你通过关键字的名字获取提交的数据。像是flask中的request.form。这个例子中,request.POST[‘Choice’]以字符串形式返回选择的Chouce的ID。request.POST的值永远是字符串。注意,Django还以同样的方式提供requtst.GET用于访问GET数据–但我们在代码中显示地使用request.POST,以保证数据只能通过POST调用改动。

  • 如果再request.POST[‘Choice’]数据中没有提供choice,POST将引发一个KeyError。上面的代码检查KetError,如果没有给出choice将重新显示Question表单和一个错误信息。

  • 在增加Choice的得票数之后,代码返回一个HttpResponseRedirect而不是常用的HttpResponse,HttpResponseRedirect只接受一个参数:用户将要被重定向的URL。正如上面的Python注释所指出的,在成功处理POST数据之后,应该始终返回HttpResponseRedirect。这个技巧并不是Django特有的;这只是很好的Web开发实践。类似flask中的redirect重定向。

  • 在这个例子中,我们在HttpResponseRedirect的构造函数中使用reverse()函数。这个函数避免了我们在视图函数中硬编码URL。他需要我们给出想要跳转的视图的名字和该视图所对应的UTL模式中需要给该视图提供的参数。在上面这个例子中,reverse()调用将返回一个这样的字符串:

    'polls/3/results/'
    

    其中3是qeustion.id的值。重定向的URL将调用‘results’视图来显示最终的页面。

HttpRequest是一个HttpRequest对象。

当有人对问题进行投票后,vote()视图将重新重定向到问题的结果界面。让我们来编写这个视图并创建一个前端页面results.html:

polls/views.py
def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})


polls/templates/polls/results.html
<h1>{{ question.question_text }}</h1>
<ul>
  {% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}——{{ choice.votes }}票数{{ choice.votes|pluralize }}</li>
  {% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">再次投票</a>

现在,在你的浏览器中访问/polls/1/然后为Question投票。你应该看到一个投票结果页面,并且在你每次投票之后都会更新。如果你图教师没有选择任何Choice,你应该看到错误信息。

使用通用视图:代码还是少点好

detail()和results()视图都很简单——并且,想上面提到的那样,存在冗余问题。用来显示一个投票列表的index()视图和他们类似。

这些视图反应基本的Web开发中的一个常见情况:根据URL中的参数从数据库中获取数据、载入模板文件然后返回渲染后的模板。由于这种情况特别常见,Django提供一种快捷方式,叫做“通用视图”系统。

通用视图将常见的模式抽象画,可以是你在编写应用是甚至不需要编写Python代码。让我们将我们的投票应用转换成使用通用视图系统,这样我们可以删除许多我们的代码。我们仅仅需要做一下几步来完成转换:

  1. 转换URLconf。
  2. 删除一些旧的、不在需要的视图
  3. 基于Django的通用视图引入新的视图。

为什么要重构代码?一般来说,当编写一个Django应用是,你应该先评估一下通用视图是否可以解决你的问题,你应该在一开始使用它,而不是进行到一般是重构代码。本教程目前为止是有意将重点放在“以艰难的方式”编写视图,这是为将重点放在核心概念上。

改良URLconf

首先,打开polls/urls.py这个URLconf并将它修改成:

polls/urls.py
from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

注意,第二个和第三个匹配准则中,路径字符创中酦醅模式的名称已经由<question_id>改为

改良视图

下一步,我们将删除旧的index,detail和results视图,并用Django的通用视图代替。打开polls/views.py文件:

polls/views.py
class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """返回最近的五个问题列表latest_question_list"""
        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):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except(KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': "没有这个选项"})
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

我们在这里使用两个通用视图:ListView和DetailView。这两个视图分别抽象“显示一个对象列表”和“显示一个特定类型对象的详细信息页面”这两种概念。

  • 每个通用视图需要知道它将作用与哪个模型。这由model属性提供。
  • DetailView期望从URL中捕获名为“PK”的主键值,所以我们为通用视图从Question_id改成PK

默认情况下,通用视图DetailView使用一个叫做<app name>/<model name>_detail.html的模板。在我们的例子中,他将使用"polls/question_detail.html"模板。template_name属性使用来告诉Django使用一个指定的模板名字,而不是自动生成的默认名字。我们也为results列表视图制定了template_name——这确保results视图和detail视图在渲染师具有不同的外观,即使它们在后台都是同一个DetailView

类似地,ListView 使用一个叫做 <app name>/<model name>_list.html 的默认模板;我们使用 template_name 来告诉 ListView使用我们创建的已经存在的 "polls/index.html" 模板。

在之前的教程中,提供模板文件时都带有一个包含question和latest_question_list变脸的context。对于DetailView,question变量会自动提供——因为我们使用Django的模型(Question),Django能够为context比那两决定一个合适的名字。然而对于ListView,自动生成的context变量时question_list。为了覆盖这个行为,我们提供context_object_name属性,表示我们想使用latest_question_list。作为一种替换方案,你可以改变你的模板来匹配新的context变量——这是一种更便捷的方法,告诉django使用你想使用的变量名。

小结

本节主要学习的是Django的表单处理,因为在处理投票信息时要使用表单向路由传递信息,修改数据库字段,为votes加上1

因为表单会改变数据库字段,所以我们在表单中要使用(% csrf_token %)字段方式跨站点请求伪造。

在表单中,input name=的作用是为input标签起一个名字,让后台可以通过这个name值来获取传递的信息

input value=的作用是定义向后端传递的值。

在后端中定义了vote()视图函数来处理投票计数,

视图首先使用get_object_or_404()来获取一个Question对象,

然后使用request.POST[‘choice’]来获取用户提交的数据,然后通过Question对象查询到这个选项,

这里使用了try except来捕获异常,如果没有获取到Choice选项,则会返回一个错误信息给前端detail页面

如果没有异常就对Choice的votes字段+1,然后调动.save()方法保存到数据库中。

然后重定向到问题的results页面,并且携带一个参数args=(question.id,)让结果页面根据question.id显示Question的票数情况。

通用视图

要使用通用视图首先要评估一下是否适合你的项目,通用视图将常见的模式抽象化,让你用更少的代码来编写你的视图。但是通用视图还是有些麻烦,学习起来需要更多时间。

猜你喜欢

转载自blog.csdn.net/qq_43503724/article/details/85165998