跟着Django手册建立Blog(一)

来源:互联网 发布:仿手机淘宝 html5 编辑:程序博客网 时间:2024/05/02 05:01

环境: python 3.4, Django 1.11.1, pycharm2016.3
教材: Django document 1.11.2,Django 1.10翻译中文版
目标:建立一个polls(投票)的网页app.

步骤:

1. 建立mysite project

利用virtualenvwrapper建立''blogpy34'' 虚拟环境(代表编译器是python3.4), 在pycharm中建立mysite project, 过程省略.

2. pycharm中新建polls app

a. pycharm菜单Tools--Run Manage.py Task
b. 在pycharm下方的命令行中输入startapp polls


c. 编辑polls app中views代码
from django.shortcuts import renderfrom django.http import HttpResponsedef index(request):  # 每一个def定义了根据request请求而返回的HttpResponse响应.    return HttpResponse("Hello,world. You're at the polls index.")
d. 新建并编辑polls app中urls
from django.conf.urls import urlfrom . import viewsurlpatterns = [    url(r'^$', views.index, name = 'index'),]  # 正则表达式匹配浏览器输入的url, 匹配上的就返回views中index响应.
e. 配置root urls, 关联app中urls

在第一条url后面插入第二条url:  url(r'^polls/', include('polls.urls')),

3. 设置数据库

 a. 设置数据库引擎
在settings中DATABASES中设定后端引擎为sqlite3(自己选择).

b. pycharm命令行中执行migrate
settings中installed app在执行时需要使用数据库中的表, 为了它们能正常运行, 需要为它们建立数据库中的表.

project目录下的db.sqlite3将发生变化, 根据installed app来创建表格

 

4. 新建Models

a. 为poll app创建Question和Choice model
在polls app下的models中输入如下代码:
from django.db import modelsclass Question(models.Model):    question_test = models.CharField(max_length=200)  # "question_test"将会成为数据表中的列名.    pub_date = models.DateTimeField('date published')  # 因有对pub_date重新命名, 数据表中列名是'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)

5.激活Models

 a. installed apps注册自己的app
在settings中的installed apps中增加一条: polls.apps.PollsConfig

b. 输入makemigrations polls

在pycharm命令行中输入makemigrations polls, 在polls migrations下增加一个0001.initial.py

c. 输入sqlmigrate polls 0001
命令行中将出现以下动作:
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_test" 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, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id"));
INSERT INTO "polls_choice" ("question_id", "votes", "choice_text", "id") SELECT NULL, "votes", "choice_text", "id" FROM "polls_choice__old";
DROP TABLE "polls_choice__old";
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;


Process finished with exit code 0

d. 输入migrate
输入前, 数据库状态

输入后, 数据库状态:
db下增加2个表格, polls_choice和polls_question, 在django_migrations表格中新增一条polls 0001的记录, 如果该表格中已存在polls 0001, django检查到后将不新建choice和question表格.


重温变更models的三部曲:
① 在models.py中变更models
②运行makemigrations为那些变更生成migrations, migrations可视为migrate的对象.
③运行migrate, 将那些变更migrate到数据库中.

6. 探索数据库 API

a. 在manage.py的命令行中输入shell, 则进入python交互环境.
b. 输入如下代码:
>>>from polls.models import Question, Choice>>>Question.objects.all()<QuerySet []>>>>from django.utils import timezone>>>q = Question(question_text="What's new?", pub_date=timezone.now())>>>q.save()>>>q.id1>>>q.question_text"What's new?">>>q.pub_datedatetime.datetime(2017, 6, 5, 14, 21, 50, 920284, tzinfo=<UTC>)>>>q.question_text = "What's up?">>>q.save()>>>Question.objects.all()<QuerySet [<Question: Question object>]>
最后一句中的<Question: Question object>不能很好的表明该对象身份, 为了改善, 需要在Question models中为Question和Choice类增加__str__()方法.
class Question(models.Model):    ......    def __str__(self):        return self.question_text    def was_published_recently(self):  # 该方法只是用来演示, 这只是普通的python方法.        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)class Choice(models.Model):    ......    def __str__(self):        return self.choice_text
继续探索数据库API
>>> from polls.models import Question, Choice
# 确认下__str__()是否生效>>> Question.objects.all()<QuerySet [<Question: What's up?>]>
# Django提供了丰富的数据库API, 完全以keyword参数驱动.>>> 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)<Question: What's up?>
#请求一个不存在的ID, 会引发报错.>>> Question.objects.get(id=2)Traceback (most recent call last):......polls.models.DoesNotExist: Question matching query does not exist.
# 查找某个主键>>> Question.objects.get(pk=1)<Question: What's up?>
# 验证下自定义的was_published_recently方法是否生效>>> q = Question.objects.get(pk=1)>>> q.was_published_recently()True
# 为某个问题建立好几个选项, Django会用一个集合装载外键关系的另外一侧, 如Question' choice 
>>> q = Question.objects.get(pk=1)
>>> q.choice_set.all()<QuerySet []> # 目前主键为1的问题下面, 没有任何选项.
# 为问题建立选项>>> 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>>>> q.choice_set.create(choice_text='Just hacking again', votes=0)<Choice: Just hacking again>>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# choice可以回溯它的问题.>>> c.question<Question: What's up?>>>> q.choice_set.all()<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>, <Choice: Just hacking again>]>>>> q.choice_set.count()4>>> Choice.objects.filter(question__pub_date__year=current_year)<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>, <Choice: Just hacking again>]>>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')>>> c.delete()(2, {'polls.Choice': 2})

7.引入Django Admin

a. 创建superuser
manage.py@mysite > createsuperuser
然后按提示输入用户名, 邮箱, 密码.

b. 开启开发服务器
manage.py@mysite > runserver

c. 浏览器输入"http://127.0.0.1:8000/admin/"
输入账号密码后看到界面:
看到的Groups和Users是由django.contrib.auth生成的.

8. 让polls app在admin中可修改.

此时在admin中看不到我们建立的polls app, 要显示的话, 需要告诉admin, Question对象在admin中是有界面的.
a. 修改polls admin
from django.contrib import adminfrom .models import Question# Register your models here.admin.site.register(Question)
b. 在浏览器检查效果


c. 进入Question修改页面

d. 点击其中一个Question修改页面


可以看到, 字符类型的Question text与日期类型的Date published, 可以自动的以合适的方式显示. 在此页面可以对键进行修改, 修改之后按保存, 保存之后按History就能看到修改履历.

9. 创建views

网站的每个页面是由一个个views生成的, views是函数或者类的方法.
现在为polls app添置更多的views: detail, result, vote.

a. 在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)

b. 将以上views绑定到urls中.
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'),]

c. 在浏览器中测试
浏览器中输入网址/polls/12,, 验证.
当输入网址后, Django会加载mysite.urls Python模块, 因为在mysite.setting中已经指定了.

Django根据mysite.urls中的正则表达式, 一条条进行匹配, 匹配OK了就转入相应的views, 相应的views则返回HttpResponse.

10. 写一写"会做事"的views

a. 在polls/views.py中改写以下代码, 其余代码不动.
from .models import Questiondef 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(output)
这里有一个问题, 这一页面的设计是写死在view中的, 如果想改变页面的外观, 则必须重新编辑这部分代码. 更好的方式是利用django的template system来分离页面设计的代码到template中.
b. 在polls下创建template目录.
c. 在刚创建的template目录下创建polls目录.
d. 在刚创建的polls目录下创建index.html文件.

为何要新建一个polls文件夹, 然后把index放在里面?
因为Django没有办法区分不同app里面同样名字的template, 比如polls里面的index和polls2里面的index. 最简单的解决方式是如上面3步, 把index放在template文件夹下的polls文件夹下, 这样django就知道这个index是属于polls这个app的.

e. 修改index template
在index中输入以下代码
{% if latest_question_list %}  # 判断views那边传过来的latest_question_list元组是否为空.    <ul>  # unorder list, 不带编号的列表.        {% for question in latest_question_list %}  # 将元组中的问题循环取出.            <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>   # 取出的每个问题都包装成链接polls/{{question.id}},加{{}}的目的是为了将其"变成一个变量"(不严谨说法)         {% endfor %}    </ul>    {% else %}        <p>No polls are available.</p>{% endif %}
如果忘记了question object下面有哪些键, 打开db数据库查看question表就知道了.

f. 修改views
from django.template import loaderdef index(request):    latest_question_list = Question.objects.order_by('-pub_date')[:5]    template = loader.get_template('polls/index.html')  # 加载polls/index.html这个template    context = {        'latest_question_list': latest_question_list,   # 将latest_question_list这个元组包装成字典context.    }    return HttpResponse(template.render(context, request))  # 将字典context使用index模板进行渲染.
这样在浏览器输入http://127.0.0.1:8000/polls/, 能看到question
 可以看到question有链接, 这是因为在index template中设置了链接, 链接显示的文字就是question.question_text.
<a href="/polls/{{ question.id }}/">{{ question.question_text }}
点击链接后, urls模块识别出/polls/1/之后, 指向views.detail, 生成以下页面:
g. 使用shortcut的render()
上面的代码需要调用HttpResponse, Loader模块, 语句繁琐, 更简洁的方法是调用shortcut的render().
from django.shortcuts import renderfrom django.http import HttpResponse  #没用了from .models import Questionfrom django.template import loader  #没用了def index(request):    latest_question_list = Question.objects.order_by('-pub_date')[:5]    #template = loader.get_template('polls/index.html')  # 引入shortcut的render之后, 不需要写那么多    context = {        'latest_question_list': latest_question_list,    }    #return HttpResponse(template.render(context, request))  # 引入shortcut的render之后, 不需要写那么多    return render(request, 'polls/index.html',context)

11. 弹出404错误.

a. 修改polls/views中的detail
from django.http import Http404def 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})
b. 创建detail template
在templates-polls下建立detail.html, 写入{{ question }}.


c. 快捷方式: get_object_or_404()
因为使用get()方法而找不到相应对象时弹出Http404是常用的用法, django提供了快捷方式, 下面重写detail.


12. 使用template系统

a.重写detail.html template
<h1>{{ question.question_text }}</h1>  # template系统采用一种dot-lookup语法来获得变量参数.首先在对象question中寻找,失败后在question的参数中寻找.<ul>    {% for choice in question.choice_set.all %}  #被理解成python代码"question.choice_set.all()", 即一个可迭代对象.        <li>{{ choice.choice_text }}</li>    {% endfor %}</ul>


13. 去除template中URL的硬编码(死写法)

a. 将死写法变成活写法
在polls/index.html模板中, 链接的'/polls/'是硬编码
<a href="/polls/{{ question.id }}/">{{ question.question_text }}</a>
进行改写
<a href="{% url 'detail' question.id %}">{{ question.question_text }}</a>
程序将从polls/urls.py中寻找名称为'detail'的url pattern进行url匹配.
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
当你想把url改为其他形式, 比如polls/list/1/的时候, 只需要在urls中更改, 不需要在template中更改, 实现了urls与template模块的弱耦合.

b. URL名称的命名空间
上面的URL名称是''detail'', 在当前只有一个app情况下不会造成混淆, 当有多个app时, 有可能在别的app中也有另外一个''detail'', 造成混淆.
a. 在polls/urls.py中指定app名字
from django.conf.urls import urlfrom . import viewsapp_name = 'polls'  #指定app名字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'),]
b. 修改polls/templates/polls/index.html
将其中'detail'变成'polls:detail'
<a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a>
原创粉丝点击