【Django】Django框架进阶详述(一)

  • 2020 年 2 月 14 日
  • 筆記

1、URL的配置

(1)Django如何处理一个请求

  • 项目启动后根据settings.py中的 ROOT_URLCONF决定根URLconf。
  • 它是django.conf.urls.url() 实例的一个Python 列表。
  • Django 依次匹配每个URL 模式,在与请求的URL 匹配的第一个模式停下来。
  • 一旦其中的一个正则表达式匹配上,Django 将导入并调用给出的视图,它是一个简单的Python 函数(或者一个基于类的视图)。视图将获得如下参数:
    • 一个HttpRequest 实例。
    • 如果匹配的正则表达式返回了没有命名的组,那么正则表达式匹配的内容将作为位置参数提供给视图。
    • 关键字参数由正则表达式匹配的命名组组成,但是可以被django.conf.urls.url()的可选参数kwargs覆盖。
  • 如果没有匹配到正则表达式,或者如果过程中抛出一个异常,Django 将调用一个适当的错误处理视图,比如404页面。
from django.conf.urls import url  from . import views    urlpatterns = [      url(r'^articles/2003/$', views.special_case_2003),      url(r'^articles/([0-9]{4})/$', views.year_archive),      url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),      url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),  ]

说明:

  • 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
  • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
  • 每个正则表达式前面的’r’ 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义。
  • /articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数views.month_archive(request, ‘2005’, ‘03’)。
  • /articles/2005/03/?blog=hi 和上面相同。
  • /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
  • /articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。
  • /articles/2003 不匹配任何一个模式,因为每个模式要求URL 以一个斜线结尾。
  • /articles/2003/03/03/ 请求将调用函数views.article_detail(request, year=‘2003’, month=‘03’, day=‘03’)。
  • 默认捕捉到的都是字符串。

(2)错误处理 当Django 找不到一个匹配请求的URL 的正则表达式时,或者当抛出一个异常时,Django 将调用一个错误处理视图。 默认错误处理视图:

  • handler404 —— 参见django.conf.urls.handler404。
  • handler500 —— 参见django.conf.urls.handler500。
  • handler403 —— 参见django.conf.urls.handler403。
  • handler400 —— 参见django.conf.urls.handler400。

(3)URL多种写法

from django.conf.urls import include, url    urlpatterns = [      # ... snip ...      url(r'^community/', include('django_website.aggregator.urls')),      url(r'^contact/', include('django_website.contact.urls')),      # ... snip ...  ]
from apps.main import views as main_views  from credit import views as credit_views    extra_patterns = [      url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),      url(r'^charge/$', credit_views.charge),  ]    urlpatterns = [      url(r'^$', main_views.homepage),      url(r'^help/', include('apps.help.urls')),      url(r'^credit/', include(extra_patterns)),  ]
from django.conf.urls import include, url  from . import views    urlpatterns = [      url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/', include([          url(r'^history/$', views.history),          url(r'^edit/$', views.edit),          url(r'^discuss/$', views.discuss),          url(r'^permissions/$', views.permissions),      ])),  ]
urlpatterns = [      url(r'^$', main_views.homepage),      url(r'^help/', include('apps.help.urls')),      url(r'^credit/', include(extra_patterns)),  ]    urlpatterns += [      url(r'^log/', include(other_log)),  ]

(4)捕获参数的继承

# In settings/urls/main.py  from django.conf.urls import include, url    urlpatterns = [      url(r'^(?P<username>w+)/blog/', include('foo.urls.blog')),  ]    # In foo/urls/blog.py  from django.conf.urls import url  from . import views    urlpatterns = [      url(r'^$', views.blog.index),      url(r'^archive/$', views.blog.archive),  ]

(5)传递额外选项

from django.conf.urls import url  from . import views    urlpatterns = [      url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),  ]

说明:会覆盖url中捕获的值。 (6)url反向解析

  • 在模板中:使用url 模板标签。
  • 在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
  • 在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。

2、Views

(1)返回404

from django.http import HttpResponse, HttpResponseNotFound  ...      return HttpResponse('Not found', status=404)      or      return HttpResponseNotFound('Not found')

(2)自定义错误视图

# urls.py  handler404 = 'mysite.views.my_custom_page_not_found_view'  handler500 = 'mysite.views.my_custom_error_view'  handler403 = 'mysite.views.my_custom_permission_denied_view'  handler400 = 'mysite.views.my_custom_bad_request_view'

(3)render()函数

render(request, template_name, context=None, content_type=None, status=None, using=None)[source]

request The request object used to generate this response. template_name 模板名称,列表会使用先找到的那个。 context 渲染模板的context 字典,默认是 {}。 content_type Reponse MIME type,默认使用 DEFAULT_CONTENT_TYPE setting. status The status code for the response. Defaults to 200. using The NAME of a template engine to use for loading the template. (4)redirect()函数

redirect(to, permanent=False, *args, **kwargs)

Returns an HttpResponseRedirect to the appropriate URL for the arguments passed.

The arguments could be:

  • A model: the model’s get_absolute_url() function will be called.
  • A view name, possibly with arguments: reverse() will be used to reverse-resolve the name.
  • An absolute or relative URL, which will be used as-is for the redirect location.

By default issues a temporary redirect; pass permanent=True to issue a permanent redirect.

object = MyModel.objects.get(...)  return redirect(object)    return redirect('some-view-name', foo='bar')  return redirect('/some/url/')  return redirect('https://example.com/', permanet=True)

(5)get_object_or_404()函数

get_object_or_404(klass, *args, **kwargs)[source]

Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.

Required arguments:

  • klass A Model class, a Manager, or a QuerySet instance from which to get the object.
  • **kwargs Lookup parameters, which should be in the format accepted by get() and filter().
from django.shortcuts import get_object_or_404    def my_view(request):      my_object = get_object_or_404(MyModel, pk=1)    This example is equivalent to:    from django.http import Http404    def my_view(request):      try:          my_object = MyModel.objects.get(pk=1)      except MyModel.DoesNotExist:          raise Http404("No MyModel matches the given query.")    You can also use related managers:  author = Author.objects.get(name='Roald Dahl')  get_object_or_404(author.book_set, title='Matilda')

(6)get_list_or_404()函数

get_list_or_404(klass, *args, **kwargs) 

(7)reverse 和 reverse_lazy

reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
reverse_lazy(viewname, urlconf=None, args=None, kwargs=None, current_app=None)

It is useful for when you need to use a URL reversal before your project’s URLConf is loaded. Some common cases where this function is necessary are:

>>> reverse('polls:detail', args=(123,))  '/polls/123/'  >>> reverse_lazy('polls:detail', args=(123,))  <django.utils.functional.lazy.<locals>.__proxy__ at 0x7f5a647087f0>

3、装饰器

Django provides several decorators that can be applied to views to support various HTTP features.

  • require_http_methods(request_method_list)
  • require_GET()
  • require_POST()
  • require_safe()
from django.views.decorators.http import require_http_methods    @require_http_methods(["GET", "POST"])  def my_view(request):      # I can assume now that only GET or POST requests make it this far      # ...      pass
  • gzip_page()
  • cache_control(**kwargs) This decorator patches the response’s Cache-Control header
  • never_cache()

HttpRequest and HttpResponse详见:https://docs.djangoproject.com/en/1.10/ref/request-response/

4、发送邮件

# settings.py  EMAIL_HOST = ''  EMAIL_PORT = ''  EMAIL_HOST_USER = ''  EMAIL_HOST_PASSWORD = ''  EMAIL_USE_TLS = True  EMAIL_USE_SSL = True
django.core.mail.send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)
def send_email(request):      subject = request.POST.get('subject', '')      message = request.POST.get('message', '')      from_email = request.POST.get('from_email', '')      if subject and message and from_email:          try:              send_mail(subject, message, from_email, ['[email protected]'])          except BadHeaderError:              return HttpResponse('Invalid header found.')          return HttpResponseRedirect('/contact/thanks/')      else:          # In reality we'd use a form class          # to get proper validation errors.          return HttpResponse('Make sure all fields are entered and valid.')

5、导出CSV

import csv  from django.http import HttpResponse    def some_view(request):      # Create the HttpResponse object with the appropriate CSV header.      response = HttpResponse(content_type='text/csv')      response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'        writer = csv.writer(response)      writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])      writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])        return response

6、上传文件

# polls/views.py    from django.shortcuts import render  from django.http import HttpResponse      def upload(request):      if request.method == 'POST':          upload_file = request.FILES.get('file', None)          if upload_file is None:              return HttpResponse('No file get')          else:              with open('/tmp/%s' % upload_file.name, 'wb') as f:                  f.write(upload_file.read())              return HttpResponse('Ok')      else:          return render(request, 'polls/upload.html')      # polls/templates/polls/upload.html    <form method="post" action="" enctype="multipart/form-data">      {% csrf_token %}      <lable> upload </lable>      <input type="file" name="file">      <input type="submit" value="upload">  </form>

说明:

  • request.FILES
  • enctype

7、下载文件

# polls/views.py    def download(request):      f = open('/tmp/test.csv', 'rb')      response = HttpResponse(f, content_type='application/csv')      response['Content-Disposition'] = 'attachment; filename=test.csv'      f.close()      return response

8、Template

Variables {{ var }} {{ dict.key }} {{ var.attr }} {{ var.method }} {{ var.index }}  Filter {{ list|join:"," }} {{ name|lower }}  Tags {% tag xxx %} {% endtag %} {% for ... %} ... {% endfor %}  {# comment #}

(1)配置template引擎

TEMPLATES = [      {          'BACKEND': 'django.template.backends.django.DjangoTemplates',          # 'BACKEND': 'django.template.backends.jinja2.Jinja2',          'DIRS': [],          'APP_DIRS': True,          'OPTIONS': {              # ... some options here ...          },      },  ]

(2)获取和渲染模板

  • django.template.loader.get_template(template_name, using=None)
  • django.shortcuts.render()
  • Template.render(context=None, request=None)
  • django.template.loader.render_to_string(template_name, context=None, request=None, using=None)
  • Context

(3)Context processors Context processors are functions that receive the current HttpRequest as an argument and return a dict of data to be added to the rendering context.

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',              ],          },      },  ]
  • django.contrib.auth.context_processors.auth
    • user
    • perms
  • django.template.context_processors.debug
    • debug
    • sql_query A list of {‘sql’: …, ‘time’: …} dictionaries, representing every SQL query that has happened so far during the request and how long it took. The list is in order by database alias and then by query. It’s lazily generated on access.
  • django.template.context_processors.media
    • MEDIA_URL
  • django.template.context_processors.static
    • static
  • django.template.context_processors.csrf
    • django.template.context_processors.request

Writing your own context processor:

A context processor has a very simple interface: It’s a Python function that takes one argument, an HttpRequest object, and returns a dictionary that gets added to the template context. Each context processor must return a dictionary.

(4)内置template tag和filters 详见:https://docs.djangoproject.com/en/1.10/ref/templates/builtins/ (5)自定义 template filter django寻找自定义filter的目录是 app_name/templatetags。

$ ~/projects/mysite tree polls/templatetags  polls/templatetags  ├── __init__.py  └── mytags.py    0 directories, 2 files
# polls/templatetags/mytags.py    from django import template    register = template.Library()    @register.filter(name='Lower')  def lower(text):      return text.lower()    @register.filter  def question_choice_count(question):      return question.choice_set.count()    @register.filter  def question_choice_add(question, num):      return question.choice_set.count() + int(num)    # polls/templates/polls/index.html  {% load static %}  {% load mytags %}    <img src="{% static 'django.png' %}">  {% if latest_question_list %}  <ul>    {% for question in latest_question_list %}    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }} </a>    -- {{ question|question_choice_count }} </li> -- {{ question|question_choice_count_add:2 }}    {% endfor %}  </ul>  {% endif %}

(6)模板扩展和包含

  • 扩展 extends
  • 包含 include
# mysite/templates/base.html    <html>  <head>      <title> {% block title %} {% endblock %}</title>      {% include '_head_css_js.html' %}  </head>    <body>  {% include '_header.html' %}  {% block content %}    {% endblock %}  {% include '_footer.html' %}  </body>  </html>    # mysite/templates/_header.html    <div>      This is header  </div>    # mysite/templates/_footer.html    <div>      This is footer  </div>    # mysite/templates/_head_css_js.html      # mysite/templates/index.html    {% extends 'base.html' %}  {% block content %}  This is my self content  {% endblock %}      # mysite/templates/index2.html    {% extends 'base.html' %}  {% block content %}  This is index index  {% endblock %}

说明:

  • extends是在继承模板,然后自定义可以设置的block。
  • include是导入一个模板片段到该位置。