Django基础:URL的配置

发布于 2020-11-23  909 次阅读


Django的URL是如何工作的

URL通常与视图(View)一起工作的。服务器收到用户请求后,会根据urls.py里的关系条目,去视图View里查找到与请求对应的处理方法,从而返回给客户端http页面数据。这和其它web开发的路由机制(Router)是一个道理。如果你还不知道视图是什么,那么你只需要记住:视图收到用户的请求后,展示给用户看得见的东西。

投票例子:

urlpatterns = [
    # path('', views.index, name='index'),
    # path('<int:question_id>/', views.detail, name='detail'),
    # path('<int:question_id>/results/', views.results, name='results'),
    # path('<int:question_id>/vote/', views.vote, name='vote'),

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

# polls/views.py
def index(request):
    # 投票话题列表
    latest_question_list = Question.objects.order_by('-pub_time')[:5]

    # template = loader.get_template('polls/index.html')
    # context = {
    #     'latest_question_list': latest_question_list,
    # }
    # return HttpResponse(template.render(context, request))

    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context=context)


def detail(request, question_id):
    # 投票详情
    # return HttpResponse("You're looking at question %s." % 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})

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

def results(request, question_id):
    # 投票结果
    # response = "You're looking at the results of question %s."
    # return HttpResponse(response % question_id)

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

def vote(request, question_id):
    # 投票
    # return HttpResponse("You're voting on question %s." % 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,)))

那么这段代码是如何工作的?

  • 当用户在浏览器输入http://127.0.0.1:8000/polls/时,URL收到请求后会调用视图views.py里的index方法,展示所有文章。
  • 当用户在浏览器输入/polls/<int:id>/时,URL不仅调用了views.py里的detail方法,而且还把参数文章id通过<>括号的形式传递给了视图。int这里代表只传递整数,传递的参数名字是id。

注意当你配置URL时,别忘了把你的app(比如polls)urls加入项目的URL配置里(mysite/urls.py), 如下图所示:

Django URL传递参数的方法path和_re_path

写个URL很简单,但如何通过URL把参数传递给给视图view是个技术活。Django URL提供了两种匹配方式传递参数: path和re_path。path是正常参数传递,re_path是采用正则表达式regex匹配。path和re_path传递参数方式如下:

  • path方法:采用双尖括号<变量类型:变量名>或<变量名>传递,例如<int:id>, <slug:slug>或<username>。
  • re_path方法: 采用命名组(?P<变量名>表达式)的方式传递参数。

下图两种传递文章id给视图函数的方式是一样的。re_path里引号前面的小写r表示引号里为正则表达式, 请忽略'\'不要转义,^代表开头,$代表以结尾,\d+代表正整数。

from django.urls import re_path, path

from blog import views

app_name = 'blog'

urlpatterns = [

    # 展示文章详情
    re_path(r'^article/(?P<pk>\d+)/$',
            views.ArticleDetailView.as_view(), name='article_detail'),

    path('article/<int:id>/', views.ArticleDetailView.as_view, name='article_detail')

]

URL的命名及reverse()方法

你注意到没?我们在上述代码中还给URL取了一个名字 'article_detail'。这个名字大有用处,相当于给URL取了个全局变量的名字。它可以让你能够在Django的任意处,尤其是模板内显式地引用它。假设你需要在模板中通过链接指向一篇具体文章,下面那种方式更好?

方法1: 使用命名URL
<a href="{% url 'article_detail' id %}">Article</a>

方法2: 使用常规URL - 不建议
<a href="blog/article/id">Article</a>

如果你还没意识到方法1的好处,那么想想吧,假设你需要把全部模板链接由blog/article/id改为blog/articles/id, 那种方法更快?改所有模板,还是改URL配置里的一个字母?

可惜的是命名的URL一般只在模板里使用,不能直接在视图里使用。如果我们有了命名的URL,我们如何把它转化成常规的URL在视图里使用呢?Django提供的reverse()方法很容易实现这点。假设不同的app(比如news和blog)里都有article这个命名URL, 我们怎么区分呢? 我们只需要在article前面加上blog这个命名空间即可。

from django.urls import reverse

# output blog/article/id
reverse('blog:article', args=[id])

URL如何指向基于类的视图(View)

目前path和re_path都只能指向视图view里的一个函数或方法,而不能指向一个基于类的视图(Class based view)。Django提供了一个额外as_view()方法,可以将一个类伪装成方法。这点在当你使用Django在带的view类或自定义的类时候非常重要。具体使用方式如下:

from django.urls import re_path, path

from blog import views

app_name = 'blog'

urlpatterns = [

    # 展示文章详情
    re_path(r'^article/(?P<pk>\d+)/$',
            views.ArticleDetailView.as_view(), name='article_detail'),

    path('article/<int:id>/', views.ArticleDetailView.as_view, name='article_detail')

]

# views.py
from django.shortcuts import render

# Create your views here.
from django.views.generic import DetailView
from .models import Article


class ArticleDetailView(DetailView):
    model = Article

    def get_object(self, queryset=None):
        obj = super().get_object(queryset=queryset)
        obj.viewed()
        return obj

一名测试工作者,专注接口测试、自动化测试、性能测试、Python技术。