第一个Django应用

Django教程:http://www.liujiangblog.com/course/django/2
第一个Django应用
该应用包括以下两个部分:
    一个可以让公众用户进行投票和查看投票结果的站点
    一个让可以进行增删改查的后台admin管理界面
    
    
Part 1:请求与响应
一、 新建项目
    进入你指定的项目保存目录,然后运行下面的命令:$ django-admin startproject mysite
二、 启动开发服务器
    进入mystie项目的根目录,输入下面的命令:$ python manage.py runserver
    注意: Django的开发服务器具有自动重载功能,当你的代码有修改,每隔一段时间服务器将自动更新。
三、 创建投票应用(app)
    进入mysite项目根目录,确保与manage.py文件处于同一级,输入下述命令:$ python manage.py startapp polls
四、编写第一个视图
在polls/views.py文件中,编写代码:
    from django.http import HttpResponse
    def index(request):
        return HttpResponse("Hello, world. You're at the polls index.")
现在,在polls目录中新建一个文件,名字为urls.py,在其中输入代码如下:
    from django.conf.urls import url
    from . import views
    urlpatterns = [
        url(r'^$', views.index, name='index'),
    ]
打开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),
    ]
好了,路由设置成功后,启动服务器,然后在浏览器中访问地址http://localhost:8000/polls/。


Part 2:模型与管理后台
一、数据库安装
    $ python manage.py migrate
二、创建模型
    # 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)    
三、启用模型
上面的代码看着有点少,其实包含了大量的信息,据此,Django会做下面两件事:
    创建该app对应的数据库表结构
    为Question和Choice对象创建基于Python的数据库访问API    
要将应用添加到项目中,需要在INSTALLED_APPS设置中增加指向该应用的配置文件的链接。
我们需要再运行下一个命令:$ python manage.py makemigrations polls
有一个叫做sqlmigrate的命令可以展示SQL语句,例如:$ python manage.py sqlmigrate polls 0001
如果你感兴趣,也可以运行python manage.py check命令,它将检查项目中的错误,并不实际进行迁移或者链接数据库的操作。
四、使用模型的API
要进入Python的shell,请输入命令:$ python manage.py shell
当你进入shell后,尝试一下下面的API吧:
>>> from polls.models import Question, Choice # 导入我们写的模型类
    # 现在系统内还没有questions对象
    >>> Question.objects.all()
    <QuerySet []>
    # 创建一个新的question对象
    # Django推荐使用timezone.now()代替python内置的datetime.datetime.now()
    # 这个timezone就来自于Django唯一的依赖库pytz
    from django.utils import timezone
    >>> q = Question(question_text="What's new?", pub_date=timezone.now())
    # 你必须显式的调用save()方法,才能将对象保存到数据库内
    >>> q.save()
    # 默认情况,你会自动获得一个自增的名为id的主键
    >>> q.id
    1
    # 通过python的属性调用方式,访问模型字段的值
    >>> q.question_text
    "What's new?"
    >>> q.pub_date
    datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
    # 通过修改属性来修改字段的值,然后显式的调用save方法进行保存。
    >>> q.question_text = "What's up?"
    >>> q.save()
    # objects.all() 用于查询数据库内的所有questions
    >>> Question.objects.all()
    <QuerySet [<Question: Question object>]>
返回polls/models.py文件,修改一下question和Choice这两个类,代码如下:
    from django.db import models
    from django.utils.encoding import python_2_unicode_compatible
    class Question(models.Model):
        # ...
        def __str__(self):   
            return self.question_text
    class Choice(models.Model):
        # ...
        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 shell,再来看看其他的API:
    >>> 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)
    <Question: What's up?>
    # 查询一个不存在的ID,会弹出异常
    >>> Question.objects.get(id=2)
    Traceback (most recent call last):
    ...
    DoesNotExist: Question matching query does not exist.
    # Django为主键查询提供了一个缩写:pk。下面的语句和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
    # 让我们试试主键查询
    >>> q = Question.objects.get(pk=1)
    # 显示所有与q对象有关系的choice集合,目前是空的,还没有任何关联对象。
    >>> q.choice_set.all()
    <QuerySet []>
    # 创建3个choices.
    >>> 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对象也可通过API访问关联的Choice对象
    >>> q.choice_set.all()
    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
    >>> q.choice_set.count()
    3
    # API会自动进行连表操作,通过双下划线分割关系对象。连表操作可以无限多级,一层一层的连接。
    # 下面是查询所有的Choices,它所对应的Question的发布日期是今年。(重用了上面的current_year结果)
    >>> Choice.objects.filter(question__pub_date__year=current_year)
    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
    # 使用delete方法删除对象
    >>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
    >>> c.delete()
五、admin后台管理站点
    首先,我们需要通过下面的命令,创建一个可以登录admin站点的用户:$ python manage.py createsuperuser
    输入用户名:Username: admin
    输入邮箱地址:Email address: [email protected]
    输入密码:
        Password: larken0627
        Password (again): larken0627
        Superuser created successfully.
2. 启动开发服务器
    服务器启动后,在浏览器访问http://127.0.0.1:8000/admin/。你就能看到admin的登陆界面了:
3. 进入admin站点
    当前只有两个可编辑的内容:groups和users。它们是django.contrib.auth模块提供的身份认证框架。
4. 在admin中注册投票应用
现在还无法看到投票应用,必须先在admin中进行注册,告诉admin站点,请将polls的模型加入站点内,接受站点的管理。
    打开polls/admin.py文件,加入下面的内容:
    from django.contrib import admin
    from .models import Question
    admin.site.register(Question)
4. admin站点的主要功能
注册question模型后,刷新admin页面就能看到Question栏目了。


Part 3:视图和模板
一、概述
二、编写视图
下面,打开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.py文件中加入下面的url模式,将其映射到我们上面新增的视图。
    from django.conf.urls import url
    from . import views
    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'),
    ]
三、编写能实际干点活的视图
下面是一个新的index()视图,用于替代先前无用的index,它会根据发布日期显示最近的5个投票问卷。
    from django.http import HttpResponse
    from .models import Question
    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(output)
现在,将下列代码写入文件polls/templates/polls/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.html文件生效:
    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))
快捷方式:render()
在实际运用中,加载模板、传递参数,返回HttpResponse对象是一整套再常用不过的操作了,为了节省力气,Django提供了一个快捷方式: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()函数的第一个位置参数是请求对象(就是view函数的第一个参数),第二个位置参数是模板。还可以有一个可选的第三参数,一个字典,包含需要传递给模板的数据。最后render函数返回一个经过字典数据渲染过的模板封装而成的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})
快捷方式:get_object_or_404()
就像render函数一样,Django同样为你提供了一个偷懒的方式,替代上面的多行代码,那就是get_object_or_404()方法,参考下面的代码: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})    
五、 使用模板系统    
detail()视图会将上下文变量question传递给对应的polls/templates/polls/detail.html模板,修改该模板的内容,如下所示:
<h1>{{ question.question_text }}</h1>
<ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}</li>
    {% endfor %}
</ul>    
六、删除模板中硬编码的URLs    
七、URL names的命名空间    


Part 4:表单和类视图
一、表单form
为了接收用户的投票选择,我们需要在前端页面显示一个投票界面。让我们重写先前的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>    
现在,让我们创建一个处理提交过来的数据的视图。前面我们已经写了一个“占坑”的vote视图的url(polls/urls.py):
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),    
以及“占坑”的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):
            # 发生choice未找到异常时,重新返回表单页面,并给出提示信息
            return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            # 成功处理数据后,自动跳转到结果页面,防止用户连续多次提交。
            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))    
当有人对某个问题投票后,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/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>    
如果你在前面漏掉了一部分操作没做,比如没有创建choice选项对象,那么可以按下面的操作,补充一下:
    F:\Django_course\mysite>python manage.py shell
    Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from polls.models import Question
    >>> q = Question.objects.get(pk=1)
    >>> q.choice_set.create(choice_text='Not much', votes=0)
    <Choice: Choice object>
    >>> q.choice_set.create(choice_text='The sky', votes=0)
    <Choice: Choice object>
    >>> q.choice_set.create(choice_text='Just hacking again', votes=0)
    <Choice: Choice object>    
二、 使用类视图:减少重复代码    
1.改良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'),
    ]    
2.修改视图
接下来,打开polls/views.py文件,删掉index、detail和results视图,替换成Django的类视图,如下所示:
    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):
        """返回最近发布的5个问卷."""
            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):
    ... # 这个视图未改变!!!    

    
Part 5:测试    
Part 6:静态文件    
一、使用静态文件
首先在你的polls目录中创建一个static目录。Django将在那里查找静态文件,这与Django在polls/templates/中寻找对应的模板文件的方式是一致的。    
将下面的代码写入样式文件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' %}" />    
二、添加背景图片    
下面,我们在polls/static/polls/目录下创建一个用于存放图片的images子目录,在这个子目录里放入`background.gif文件。换句话说,这个文件的路径是polls/static/polls/images/background.gif。
在css样式文件polls/static/polls/style.css中添加下面的代码:
    body {
        background: white url("images/background.gif") no-repeat right bottom;
    }    
三、直接访问静态文件
实际上不管是在Django开发服务器上,还是在nginx+uwsgi+django部署的服务器上,都可以直接通过url访问静态文件,不需要在Django中专门为每个静态文件编写url路由和视图。
    

Part 7:自定义admin站点
一、定制模型表单
下面是一个修改admin表单默认排序方式的例子。修改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)
还有,当表单含有大量字段的时候,你也许想将表单划分为一些字段的集合。再次修改polls/admin.py:
    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)
二、添加关联对象
三、定制实例列表页面
四、定制admin整体界面
很明显,在每一个项目的admin页面顶端都显示Django administration是很可笑的,它仅仅是个占位文本。利用Django的模板系统,我们可以快速修改它。
1.定制项目模板
在manage.py文件同级下创建一个templates目录。然后,打开设置文件mysite/settings.py,在TEMPLATES条目中添加一个DIRS选项:
五、定制admin首页
六、源码对照

猜你喜欢

转载自www.cnblogs.com/larken/p/9627040.html