天天看點

【Django】Django架構進階詳述(一)

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是導入一個模闆片段到該位置。

繼續閱讀