<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                {% raw %} # 第三章-表單和視圖 *************** 在本章,我們學習以下內容: * 傳遞HttpRequest到表單 * 利用表單的save方法 * 上傳圖片 * 使用django-crispy-forms生成表單布局 * 過濾對象列表 * 管理分頁列表 * 編寫類視圖 * 生成PDF文檔 ## 引言 當數據庫結構定在模型中時,我們需要有一些視圖供用戶輸入數據,或者對用戶顯示數據。本章,我們會關注管理表單的視圖,列表視圖,以及生成可替換的輸出而不僅僅是HTML。舉個最簡單的例子來說,我們將把模板和URL規則的創建的決定權下放給你。 ## 傳遞HttpRequest到表單 Django的每一個視圖的第一個參數通常是命名為`request`的`HttpRequest`對象。它包含了請求的元數據,例如,當前語言編碼,當前用戶,當前cookie,或者是當前的session。默認,表單被用在視圖中用來接受GET或者POST參數,文件,初始化數據,以及其他的參數,但卻不是`HttpRequest`對象暗溝。某些情況下,特別是當你想要使用請求數據過濾出表單字段的選擇,又或者是你想要處理像在表單中保存當前用戶或者當前IP這樣事情時,額外地傳遞地`HttpRequest`到表單會非常有用的。 在本方法中,我會向你展示一個在表單中某人可以選擇一個用戶并對此用戶發送消息的例子。我們會傳遞`HttpRequest`對象到表單以暴露接受選項的當前用戶:我們不想要任何人都可以給他們發送消息。 ## 預熱 讓我們來創建一個叫做`email_messages`的應用,并把它放到設置中的`INSTALLED_APPS`去。該app僅含有表單和視圖而沒有模型。 ## 具體做法 1. 添加一個新文件,`forms.py`,其中消息表單包含兩個字段:接收人選項和消息文本。同時,該表單擁有一個初始化方法,它會接受`request`對象并對接收人的選項字段修改: ```python # -*- coding: UTF-8 -*- from django import forms from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User class MessageForm(forms.Form): recipient = forms.ModelChoiceField( label=_("Recipient"), queryset=User.objects.all(), required=True, ) message = forms.CharField( label=_("Message"), widget=forms.Textarea, required=True, ) def __init__(self, request, *args, **kwargs): super(MessageForm, self).__init__(*args, **kwargs) self.request = request self.fields['recipient'].queryset = self.fields['recipient'].queryset.exclude(pk=request.user.pk) ``` 2. 然后,利用`message_to_user`視圖創建`views.py`以處理表單。如你所見,`request`對象作為第一個參數被傳遞到了表單: ```python #email_messages/views.py # -*- coding: UTF-8 -*- from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from forms import MessageForm @login_required def message_to_user(request): if request.method == "POST": form = MessageForm(request, data=request.POST) if form.is_valid(): # do something with the form return redirect("message_to_user_done") else: form = MessageForm(request) return render(request, "email_messages/message_to_user.html", {'form:form'}) ``` ## 工作原理 在初始化方法中,我們擁有表現表單自身實例的`self`變量,然后是新近添加的`request`變量,在然后是位置參數(`*args`)和命名參數(`**kwargs`)。我們調用`super`構造方法傳遞所有的位置參數以及命名參數,這樣表單就可以正確地初始化。接著,我們把`request`變量賦值到一個新的表單的`request`變量,以便之后在表單的其他方法中可以訪問。再然后,我們修改接收人選項字段的`queryset`屬性以便從request暴露當前用戶。 在視圖中,我們將`HttpRequest`作為兩種場合下的第一個參數來傳遞:當表單第一次載入時,以及表單發布之后。 ## 參閱 表單的save方法的使用 ## 表單的save方法使用 為了讓視圖變得簡潔和簡單,最好的做法是移動表單數據的處理到表單本身,不論是否可行。常見的做法是利用一個可以保存數據的save方法來執行搜索,或者來完成其他的復雜的行為。我們利用save方法擴展之前方法中所定義的表單,save方法會發送電子郵件到所選擇的接收人。 ## 預備開始 我們從定義在`傳遞HttpRequest到表單`這個例子開始。 ## 具體做法 執行以下兩步: 1. 在應用的表單中導入函數以便發送郵件。然后添加save方法并嘗試發送郵件到所選擇的接收人,發生錯誤時靜默: ```python #email_messages/forms.py # -*- coding: UTF-8 -*- from django import forms from django.utils.translation import ugettext, ugettext_lazy as _ from django.core.mail import send_mail from django.contrib.auth.models import User class MessageForm(forms.Form): recipient = forms.ModelChoiceField( label=_("Recipient"), queryset=User.objects.all(), required=True, ) message = forms.CharField( label=_("Message"), widget=forms.Textarea, required=True, ) def __init__(self, request, *args, **kwargs): super(MessageForm, self).__init__(*args, **kwargs) self.request = request self.fields['recipient'].queryset = \ self.fields['recipient'].queryset.\ exclude(pk=request.user.pk) def save(self): cleaned_data = self.cleaned_data send_mail( subject=ugettext("A message from %s") % \ self.request.user, message=cleaned_data['message'], from_email=self.request.user.email, recipient_list=[cleaned_data['recipient'].email], fail_silently=True, ) ``` 2. 最后,假如發送的數據有效,則在視圖中調用save方法: ```python #email_messages/views.py # -*- coding: UTF-8 -*- from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from forms import MessageForm @login_required def message_to_user(request): if request.method == "POST": form = MessageForm(request, data=request.POST) if form.is_valid(): form.save() return redirect("message_to_user_done") else: form = MessageForm(request) return render(request, "email_messages/message_to_user.html",{'form': form}) ``` ## 工作原理 首先讓我們看下表單。save方法使用來自表單的清潔數據來讀取接收人的電郵地址,以及電郵信息。電郵的發送人就是當前的request中的用戶。假如電郵由于不正確的郵件服務器配置或者其他原因導致未能發送,那么錯誤也是靜默的,即,不會在表單中拋出錯誤。 現在,我們來看下視圖。當用戶發送的表單有效時,表單的save方法會被調用,接著用戶被重定向到成功頁面。 ## 參閱 傳遞HttpRequest到表單 ## 上傳圖片 于此做法中,我們會看到處理圖片上傳的最簡單的辦法。你會見到一個應用中訪客可以上傳勵志名言圖片的例子。 ## 預熱 首先,讓我們來創建一個應用`quotes`,并把它放到設置中的`INSTALLED_APPS`。然后,我們添加一個擁有三個字段的`InspirationalQuote`模型:作者,名言內容,以及圖片,一如下面所示: ```python #quotes/models.py #-*- coding:utf-8 -*- import os from django.db import models from django.utils.timezone import now as timezone_now from django.utils.translation impot ugetext_lazy as _ def upload_to(instance, filename): now = timezone_now() filename_base, filename_ext = os.path.splitext(filename) return 'quotes{}{}'.format(now.strftime("%Y/%m/%Y%m%d%H%M%S"), filename_ext.lower(),) class InspirationalQuote(models.Model): author = models.CharField(_("Author"), max_length=200) quote = models.TextField(_("Quote")) picture = mdoels.ImageField(_("Picture"), upload_to=upload_to, blank=True, null=True, ) class Meta: verbose_name = _("Inspirational Quote") verbose_name_plural = _("Inspiration Quotes") def __unicode__(self): return self.quote ``` 此外,我們創建了一個函數`upload_to`,該函數設置類似`quotes/2014/04/20140424140000`這樣的圖片上傳路徑。你也看到了,我們使用日期時間戳作為文件名以確保文件的唯一性。我們傳遞該函數到`picture`圖片字段。 ## 具體做法 創建`forms.py`文件,并于其中編寫一個簡單的模型表單: ```python #quotes/forms.py #-*- coding:utf-8 -*- from django import forms from models import InspirationQuote class InspirationQuoteForm(forms.ModelForm): class Meta: model = InspirationQuote ``` 在`views.py`文件中寫入一個視圖以處理表單。不要忘了傳遞類字典對象`FILES`到表單。如下,當表單有效時便會觸發`save`方法: ```python #quotes/views.py # -*- coding: UTF-8 -*- from django.shortcuts import redirect from django.shortcuts import render from forms import InspirationQuoteForm def add_quote(request): if request.method == 'POST': form = InspirationQuoteForm( data=request.POST, files=request.FIELS, ) if form.is_valid(): quote = form.save() return redirect("add_quote_done") else: form = InspirationQuoteForm() return render(request, "quotes/change_quote.html", {'form': form}) ``` 最后,在`templates/quotes/change_quote.html`中給視圖創建一個模板。為HTML表單設置`enctype`屬性為`“multipart/form-data”`十分的重要,否則文件上傳不會起作用的: ```python {% extends "base.html" %} {% load i18n %} {% block content %} <form method="post" action="" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <button type="submit">{% trans "Save" %} </button> </form> {% endblock %} ``` ## 工作原理 Django的模型表單通過模型生成的表單。它們提供了所有的模型字段,因此你不要重復定義這些字段。前面的例子中,我們給`InspirationQuote`模型生成了一個模型表單。當我們保存表單時,表單知道如何包每個字段都保存到數據中去,也知道如何上傳圖片并在媒體目錄中保存這些圖片。 ## 還有更多 作為獎勵,我會想你演示一個如何從已經上傳的圖片中生成一個縮略圖。利用這個技術,你也可以生成多個其他圖片特定的版本,不如,列表版本,移動設備版本,桌面電腦版本。 我們添加三個方法到模型`InspirationQuote`(quotes/modles.py)。它們是save,create_thumbnail,和get_thumbnail_picture_url。當模型被保存時,我們就觸發了縮略圖的創建。如下,當我們需要在一個模板中顯示縮略圖時,我們可以通過使用`{{ quote.get_thumbnail_picture_url }}`來獲取圖片的URL: ```python #quotes/models.py class InspirationQuote(models.Model): # ... def save(self, *args, **kwargs): super(InspirationQuote, self).save(*args, **kwargs) # 生成縮略圖 self.create_thumbnail() def create_thumbnail(self): from django.core.files.storage import default_storage as \ storage if not self.picture: return "" file_path = self.picture.name filename_base, filename_ext = os.path.splitext(file_path) thumbnail_file_path = "%s_thumbnail.jpg" % filename_base if storage.exists(thumbnail_file_path): # if thumbnail version exists, return its url path # 如果縮略圖存在,則返回縮略圖的url路徑 return "exists" try: # resize the original image and # return URL path of the thumbnail version # 改變原始圖片的大小并返回縮略圖的URL路徑 f = storage.open(file_path, 'r') image = Image.open(f) width, height = image.size thumbnail_size = 50, 50 if width > height: delta = width - height left = int(delta/2) upper = 0 right = height + left lower = height else: delta = height - width left = 0 upper = int(delta/2) right = width lower = width + upper image = image.crop((left, upper, right, lower)) image = image.resize(thumbnail_size, Image.ANTIALIAS) f_mob = storage.open(thumbnail_file_path, "w") image.save(f_mob, "JPEG") f_mob.close() return "success" except: return "error" def get_thumbnail_picture_url(self): from PIL import Image from django.core.files.storage import default_storage as \ storage if not self.picture: return "" file_path = self.picture.name filename_base, filename_ext = os.path.splitext(file_path) thumbnail_file_path = "%s_thumbnail.jpg" % filename_base if storage.exists(thumbnail_file_path): # if thumbnail version exists, return its URL path # 如果縮略圖存在,則返回圖片的URL路徑 return storage.url(thumbnail_file_path) # return original as a fallback # 返回一個圖片的原始url路徑 return self.picture.url ``` 之前的方法中,我們使用文件存儲API而不是直接地與應對文件系統,因為我們之后可以使用亞馬遜的云平臺,或者其他的存儲服務和方法來與默認的存儲切換也可以正常工作。 縮略圖的創建具體是如何工作的?如果原始圖片保存為`quotes/2014/04/20140424140000.png`,我們 ## 參見 使用django-crispy-forms創建表單布局 ## 使用django-crispy-forms創建表單布局 Django的應用,`django-crispy-forms`允許你使用下面的CSS框架構建,定制,重復使用表單:`Uni-Form`, `Bootstrap`, 或者`Foundation`。django-crispy-form的用法類似于Django自帶管理中的字段集合,而且它更高級,更富于定制化。按照Python代碼定義表單布局,而且你不需要太過關心HTML中的每個字段。假如你需要添加指定的HTML屬性或者指定的外觀,你仍舊可以輕松地做到。 這個方法中,我們會向你演示一個如何使用擁有Bootstrap 3——它是開發響應式,移動設備優先的web項目的最流行的前端框架——的django-crispy-forms。 ## 預熱 我們一步接一步地執行這些步驟: 1.從http://getbootstrap.com/下載前端框架Bootstrap并將CSS和JavaScript集成到模板。更多內容詳見第四章-模板和Javascript中的`管理base.html模板`。 2.使用下面的命令在虛擬環境中安裝django-crispy-forms: ```python (myproject_env)$ pip install django-crispy-forms ``` 3.確保csirpy-form添加到了`INSTALLED_APPS`,然后在該項目中設置`bootstrap3`作為模板包來使用: ```python #settings.py INSTALLED_APPS = ( # ... "crispy_forms", ) # ... CRISPY_TEMPLATE_PACK = "bootstrap3" ``` 4.讓我們來創建一個應用`bulletin_board`來闡明django-crispy-forms的用法,并把應用添加到設置中的`INSTALLED_APPS`。我們會擁有一個含有這些字段的`Bulletin`模型:類型,名稱,描述,聯系人,電話,電郵,以及圖片: ```python #bulletin_board/models.py # -*- coding: UTF-8 -*- from django.db import models from django.utils.translation import ugettext_lazy as _ TYPE_CHOICES = ( ('searching', _("Searching")), ('offering', _("Offering")), ) class Bulletin(models.Model): bulletin_type = models.CharField(_("Type"), max_length=20, choices=TYPE_CHOICES) title = models.CharField(_("Title"), max_length=255) description = models.TextField(_("Description"),max_length=300) contact_person = models.CharField(_("Contact person"), max_length=255) phone = models.CharField(_("Phone"), max_length=200, blank=True) email = models.EmailField(_("Email"), blank=True) image = models.ImageField(_("Image"), max_length=255, upload_to="bulletin_board/", blank=True) class Meta: verbose_name = _("Bulletin") verbose_name_plural = _("Bulletins") ordering = ("title",) def __unicode__(self): return self.title ``` ## 具體做法 Let's add a model form for the bulletin in the newly created app. We will attach a form helper to the form itself in the initialization method. The form helper will have the layout property, which will define the layout for the form, as follows: ```python #bulletin_board/forms.py # -*- coding: UTF-8 -*- from django import forms from django.utils.translation import ugettext_lazy as _, ugettext from crispy_forms.helper import FormHelper from crispy_forms import layout, bootstrap from models import Bulletin class BulletinForm(forms.ModelForm): class Meta: model = Bulletin fields = ['bulletin_type', 'title', 'description', 'contact_person', 'phone', 'email', 'image'] def __init__(self, *args, **kwargs): super(BulletinForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = "" self.helper.form_method = "POST" self.fields['bulletin_type'].widget = forms.RadioSelect() # delete empty choice for the type del self.fields['bulletin_type'].choices[0] self.helper.layout = layout.Layout( layout.Fieldset( _("Main data"), layout.Field("bulletin_type"), layout.Field("title", css_class="input-block-level"), layout.Field("description", css_class="input-blocklevel", rows="3"), ), layout.Fieldset( _("Image"), layout.Field("image", css_class="input-block-level"), layout.HTML(u"""{% load i18n %} <p class="help-block">{% trans "Available formats are JPG, GIF, and PNG. Minimal “size is 800 × 800 px." %}</p> """), title=_("Image upload"), css_id="image_fieldset", ), layout.Fieldset( _("Contact"), layout.Field("contact_person", css_class="input-blocklevel"), layout.Div( bootstrap.PrependedText("phone", """<span class="glyphicon glyphicon-earphone"></span>""", css_class="inputblock-level"), bootstrap.PrependedText("email", "@", css_class="input-block-level", placeholder="contact@example.com"), css_id="contact_info", ), ), bootstrap.FormActions( layout.Submit('submit', _('Save')), ) ) ``` 要渲染模板中的表單,如下,我們只需載入標簽冷酷`crispy_forms_tags`,然后使用模板標簽`{% crispy %}`: ```python #templates/bulletin_board/change_form.html} {% extends "base.html" %} {% load crispy_forms_tags %} {% block content %} {% crispy form %} {% endblock %} ``` ## 工作原理 擁有新聞簡報表的頁面的樣子大概如此: 圖片:略 As you see, the fields are grouped by fieldsets. The first argument of the Fieldset object defines the legend, the other positional arguments define fields. You can also pass named arguments to define HTML attributes for the fieldset; for example, for the second fieldset, we are passing title and css_id to set the HTML attributes title and id. 如你所見,字段是由字段集合組成的。 Fields can also have additional attributes passed by named arguments, for example, for the description field, we are passing css_class and rows to set the HTML attributes class and rows. 字段也可以通過傳遞命名參數擁有額外的屬性,例如, Besides the normal fields, you can pass HTML snippets as this is done with the help block for the image field. You can also have prepended-text fields in the layout, for example, we added a phone icon to the phone field, and an @ sign for the email field. As you see from the example with contact fields, we can easily wrap fields into HTML <div> elements using Div objects. This is useful when specific JavaScript needs to be applied to some form fields. 除了常規字段,你可以傳遞HTML片段 The action attribute for the HTML form is defined by the `form_action` property of the form helper. The method attribute of the HTML form is defined by the form_method property of the form helper. Finally, there is a Submit object to render the submit button, which takes the name of the button as the first positional argument, and the value of the button as the second argument. ## 還有更多 For the basic usage, the given example is more than necessary. However, if you need specific markup for forms in your project, you can still overwrite and modify templates of the `django-crispy-forms` app, as there is no markup hardcoded in Python files, but rather all the generated markup is rendered through the templates. Just copy the templates from the django-crispy-forms app to your project's template directory and change them as you need. 為了說明基本用法,給出例子是很有必要的一件事。不過,加入你需要在項目中為表單指定裝飾,你仍然可以重寫并修改`django-crispy-forms`這個應用的模板,在Python文件中不僅不存在由裝飾的硬編碼。 ## 參閱 * *過濾對象列表* * *管理分頁對象* ## 過濾對象列表 在web開發中,除了視圖和表單,擁有對象列表視圖和詳細視圖是很典型的情況。列表視圖可以簡單的排列對象的順序,例如,按找首字母排序或者創建日期來調用,不過對于非常龐大的數據來說就不是那么的友好了。 ## 預備工作 For the filtering example, we will use the Movie model with relations to genres, directors, and actors to filter by. It will also be possible to filter by ratings, which is PositiveIntegerField with choices. Let's create the movies app, put it into INSTALLED_APPS in the settings (movies/models.py), and define the mentioned models in the new app: 為了說明過濾例子,我們會使用關聯了種類、導演與演員的Moive模型進行過濾。而且通過評級過濾也是可以的,這是一個含有PositiveIntegerField的多選列表。我們來創建應用movies,并將它放到設置文件中的INSTALLED_APPS,然后在這個新應用中定義前面提及的模型: ```python #movies/models.py # -*- coding: UTF-8 -*- from django.db import models from django.utils.translation import ugettext_lazy as _ RATING_CHOICES = ( (1, u"?"), (2, u"??"), (3, u"???"), (4, u"????"), (5, u"?????"), ) class Genre(models.Model): title = models.CharField(_("Title"), max_length=100) def __unicode__(self): return self.title class Director(models.Model): first_name = models.CharField(_("First name"), max_length=40) last_name = models.CharField(_("Last name"), max_length=40) def __unicode__(self): return self.first_name + " " + self.last_name class Actor(models.Model): first_name = models.CharField(_("First name"), max_length=40) last_name = models.CharField(_("Last name"), max_length=40) def __unicode__(self): return self.first_name + " " + self.last_name class Movie(models.Model): title = models.CharField(_("Title"), max_length=255) genres = models.ManyToManyField(Genre, blank=True) directors = models.ManyToManyField(Director, blank=True) actors = models.ManyToManyField(Actor, blank=True) rating = models.PositiveIntegerField(choices=RATING_CHOICES) def __unicode__(self): return self.title ``` ## 具體做法 首先,我們創建能夠盡可能過濾所有目錄的`MovieFilterForm`: ```python #movies/forms.py # -*- coding: UTF-8 -*- from django import forms from django.utils.translation import ugettext_lazy as _ from models import Genre from models import Director from models import Actor from models import RATING_CHOICES class MovieFilterForm(forms.Form): genre = forms.ModelChoiceField( label=_("Genre"), required=False, queryset=Genre.objects.all(), ) director = forms.ModelChoiceField( label=_("Director"), required=False, queryset=Director.objects.all(), ) actor = forms.ModelChoiceField( label=_("Actor"), required=False, queryset=Actor.objects.all(), ) rating = forms.ChoiceField( label=_("Rating"), required=False, choices=RATING_CHOICES, ) ``` Then, we create a `movie_list` view that will use MovieFilterForm to validate the request query parameters and do the filtering by chosen categories. Note the facets dictionary, which is used here to list the categories and also the currently selected choices: 然后,我們創建一個使用MovieFilterForm驗證請求查詢參數的視圖`movie_list`,再然后通過所選擇的種類進行過濾。要注意字典這一方面,這里改字典用來列出目錄以及當前選定的選項: ```python #movies/views.py # -*- coding: UTF-8 -*- from django.shortcuts import render from models import Genre from models import Director from models import Actor from models import Movie, RATING_CHOICES from forms import MovieFilterForm def movie_list(request): qs = Movie.objects.order_by('title') form = MovieFilterForm(data=request.REQUEST) facets = { 'selected': {}, 'categories': { 'genres': Genre.objects.all(), 'directors': Director.objects.all(), 'actors': Actor.objects.all(), 'ratings': RATING_CHOICES, }, } if form.is_valid(): genre = form.cleaned_data['genre'] if genre: facets['selected']['genre'] = genre qs = qs.filter(genres=genre).distinct() director = form.cleaned_data['director'] if director: facets['selected']['director'] = director qs = qs.filter(directors=director).distinct() actor = form.cleaned_data['actor'] if actor: facets['selected']['actor'] = actor qs = qs.filter(actors=actor).distinct() rating = form.cleaned_data['rating'] if rating: facets['selected']['rating'] = (int(rating), dict(RATING_CHOICES)[int(rating)]) qs = qs.filter(rating=rating).distinct() context = { 'form': form, 'facets': facets, 'object_list': qs, } return render(request, "movies/movie_list.html", context) ``` Lastly, we create the template for the list view. We will use the facets dictionary here to list the categories and to know which category is currently selected. To generate URLs for the filters, we will use the `{% append_to_query %}` template tag, which will be described later in the Creating a template tag to modify request query parameters recipe in Chapter 5, Custom Template Filters and Tags. Copy the following code in the templates/movies/movie_list.html directory: 最后,我們為列表視圖創建模板。我們會使用 ```python {#termplates/movies/movie_list.html} {% extends "base_two_columns.html" %} {% load i18n utility_tags %} {% block sidebar %} <div class="filters"> <h6>{% trans "Filter by Genre" %}</h6> <div class="list-group"> <a class="list-group-item{% if not facets.selected.genre %} active{% endif %}" href="{% append_to_query genre="" page="" %}">{% trans "All" %}</a> {% for cat in facets.categories.genres %} <a class="list-group-item{% if facets.selected.genre == cat %} active{% endif %}" href="{% append_to_query genre=cat.pk page="" %}">{{ cat }}</a> {% endfor %} </div> <h6>{% trans "Filter by Director" %}</h6> <div class="list-group"> <a class="list-group-item{% if not facets.selected.director %} active{% endif %}" href="{% append_to_query director="" page="" %}">{% trans "All" %}</a> {% for cat in facets.categories.directors %} <a class="list-group-item{% if facets.selected.director == cat %} active{% endif %}" href="{% append_to_query director=cat.pk page="" %}">{{ cat }}</a> {% endfor %} </div> <h6>{% trans "Filter by Actor" %}</h6> <div class="list-group"> <a class="list-group-item{% if not facets.selected.actor %} active{% endif %}" href="{% append_to_query actor="" page="" %}">{% trans "All" %}</a> {% for cat in facets.categories.actors %} <a class="list-group-item{% if facets.selected.actor == cat %} active{% endif %}" href="{% append_to_query actor=cat.pk page="" %}">{{ cat }}</a> {% endfor %} </div> <h6>{% trans "Filter by Rating" %}</h6> <div class="list-group"> <a class="list-group-item{% if not facets.selected.rating %} active{% endif %}" href="{% append_to_query rating="" page="" %}">{% trans "All"%}</a> {% for r_val, r_display in facets.categories.ratings %} <a class="list-group-item{% if facets.selected.rating.0 == r_val %} active{% endif %}" href="{% append_to_query rating=r_val page="" %}">{{ r_display }}</a> {% endfor %} </div> </div> {% endblock %} {% block content %} <div class="movie_list"> {% for movie in object_list %} <div class="movie"> <h3>{{ movie.title?}}</h3> </div> {% endfor %} </div> {% endblock %} ``` ## 工作原理 If we use the Bootstrap 3 frontend framework, the end result will look like this in the browser with some filters applied: 圖片:略 So, we are using the facets dictionary that is passed to the template context, to know what filters we have and which filters are selected. To look deeper, the facets dictionary consists of two sections: the categories dictionary and the selected dictionary. The categories dictionary contains the QuerySets or choices of all filterable categories. The selected dictionary contains the currently selected values for each category. In the view, we check if the query parameters are valid in the form and then we drill down the QuerySet of objects by the selected categories. Additionally, we set the selected values to the facets dictionary, which will be passed to the template. In the template, for each categorization from the facets dictionary, we list all categories and mark the currently selected category as active. It is as simple as that. ## 參閱 *The Managing paginated lists recipe* *The Composing class-based views recipe* *The Creating a template tag to modify request query parameters recipe in Chapter 5, Custom Template Filters and Tags* ## 管理分頁列表 If you have dynamically changing lists of objects or when the amount of them can be greater than 30-50, you surely need pagination for the list. With pagination instead of the full QuerySet, you provide a fraction of the dataset limited to a specific amount per page and you also show the links to get to the other pages of the list. Django has classes to manage paginated data, and in this recipe, I will show you how to do that for the example from the previous recipe. ## 預熱 Let's start with the movies app and the forms as well as the views from the Filtering object lists recipe. ## 具體做法 At first, import the necessary pagination classes from Django. We will add pagination management to the `movie_list` view just after filtering. Also, we will slightly modify the context dictionary by passing a page instead of the movie QuerySet as `object_list`: 首先,從Django導入必需的分頁類。我們會在過濾之后將分頁管理添加到`movie_list`。而且,我們也要傳遞一個頁面而不是movie的查詢集合`object_list`來稍微修改上下文字典。 ```python #movies/views.py # -*- coding: UTF-8 -*- from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage,\ PageNotAnInteger from models import Movie from forms import MovieFilterForm def movie_list(request): qs = Movie.objects.order_by('title') # ... filtering goes here... paginator = Paginator(qs, 15) page_number = request.GET.get('page') try: page = paginator.page(page_number) except PageNotAnInteger: # If page is not an integer, show first page. page = paginator.page(1) except EmptyPage: # If page is out of range, show last existing page. page = paginator.page(paginator.num_pages) context = { 'form': form, 'object_list': page, } return render(request, "movies/movie_list.html", context) ``` In the template, we will add pagination controls after the list of movies as follows: ```python {#templates/movies/movie_list.html#} {% extends "base.html" %} {% load i18n utility_tags %} {% block sidebar %} {# ... filters go here... #} {% endblock %} {% block content %} <div class="movie_list"> {% for movie in object_list %} <div class="movie alert alert-info"> <p>{{ movie.title?}}</p> </div> {% endfor %} </div> {% if object_list.has_other_pages %} <ul class="pagination"> {% if object_list.has_previous %} <li><a href="{% append_to_query page=object_list.previous_page_number %}">&laquo;</a></li> {% else %} <li class="disabled"><span>&laquo;</span></li> {% endif %} {% for page_number in object_list.paginator.page_range %} {% if page_number == object_list.number %} <li class="active"> <span>{{ page_number }} <span class="sr-only">(current)</span></span> </li> {% else %} <li> <a href="{% append_to_query page=page_number %}">{{ page_number }}</a> </li> {% endif %} {% endfor %} {% if object_list.has_next %} <li><a href="{% append_to_query page=object_list.next_page_number %}">&raquo;</a></li> {% else %} <li class="disabled"><span>&raquo;</span></li> {% endif %} </ul> {% endif %} {% endblock %} ``` ## 工作原理 When you look at the results in the browser, you will see pagination controls like these, added after the list of movies: 圖片:略 How do we achieve that? When the QuerySet is filtered out, we create a paginator object passing the QuerySet and the maximal amount of items we want to show per page (which is 15 here). Then, we read the current page number from the query parameter, page. The next step is retrieving the current page object from the paginator. If the page number was not an integer, we get the first page. If the number exceeds the amount of possible pages, the last page is retrieved. The page object has methods and attributes necessary for the pagination widget shown in the preceding screenshot. Also, the page object acts like a QuerySet, so that we can iterate through it and get the items from the fraction of the page. The snippet marked in the template creates a pagination widget with the markup for the Bootstrap 3 frontend framework. We show the pagination controls only if there are more pages than the current one. We have the links to the previous and next pages, and the list of all page numbers in the widget. The current page number is marked as active. To generate URLs for the links, we are using the template tag {% append_to_query %}, which will be described later in the Creating a template tag to modify request query parameters recipe in Chapter 5, Custom Template Filters and Tags. ## 參閱 The Filtering object lists recipe The Composing class-based views recipe The Creating a template tag to modify request query parameters recipe in Chapter 5, Custom Template Filters and Tags ## 編寫類視圖 Django views are callables that take requests and return responses. In addition to function-based views, Django provides an alternative way to define views as classes. This approach is useful when you want to create reusable modular views or when you want to combine views out of generic mixins. In this recipe, we will convert the previously shown function-based view, movie_list, into a class-based view, MovieListView. ## 預熱 Create the models, the form, and the template like in the previous recipes, Filtering object lists and Managing paginated lists. ## 具體做法 We will need to create a URL rule in the URL configuration and add a class-based view. To include a class-based view in the URL rules, the as_view()method is used like this: ```python #movies/urls.py # -*- coding: UTF-8 -*- from django.conf.urls import patterns, url from views import MovieListView urlpatterns = patterns('', url(r'^$', MovieListView.as_view(), name="movie_list"), ) ``` Our class-based view, MovieListView, will overwrite the get and post methods of the View class, which are used to distinguish between requests by GET and POST. We will also add the get_queryset_and_facets and get_page methods to make the class more modular: ```python #movies/views.py # -*- coding: UTF-8 -*- from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage,\ PageNotAnInteger from django.views.generic import View from models import Genre from models import Director from models import Actor from models import Movie, RATING_CHOICES from forms import MovieFilterForm class MovieListView(View): form_class = MovieFilterForm template_name = 'movies/movie_list.html' paginate_by = 15 def get(self, request, *args, **kwargs): form = self.form_class(data=request.REQUEST) qs, facets = self.get_queryset_and_facets(form) page = self.get_page(request, qs) context = { 'form': form, 'facets': facets, 'object_list': page, } return render(request, self.template_name, context) def post(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def get_queryset_and_facets(self, form): qs = Movie.objects.order_by('title') facets = { 'selected': {}, 'categories': { 'genres': Genre.objects.all(), 'directors': Director.objects.all(), 'actors': Actor.objects.all(), 'ratings': RATING_CHOICES, }, } if form.is_valid(): genre = form.cleaned_data['genre'] if genre: facets['selected']['genre'] = genre qs = qs.filter(genres=genre).distinct() director = form.cleaned_data['director'] if director: facets['selected']['director'] = director qs = qs.filter(directors=director).distinct() actor = form.cleaned_data['actor'] if actor: facets['selected']['actor'] = actor qs = qs.filter(actors=actor).distinct() rating = form.cleaned_data['rating'] if rating: facets['selected']['rating'] = (int(rating), dict(RATING_CHOICES)[int(rating)]) qs = qs.filter(rating=rating).distinct() return qs, facets def get_page(self, request, qs): paginator = Paginator(qs, self.paginate_by) page_number = request.GET.get('page') try: page = paginator.page(page_number) except PageNotAnInteger: # If page is not an integer, show first page. page = paginator.page(1) except EmptyPage: # If page is out of range, show last existing page. page = paginator.page(paginator.num_pages) return page ``` ## 工作原理 No matter whether the request was called by the GET or POST methods, we want the view to act the same; so, the post method is just calling the get method in this view, passing all positional and named arguments. These are the things happening in the get method: ``` At first, we create the form object passing the REQUEST dictionary-like object to it. The REQUEST object contains all the query variables passed using the GET or POST methods. Then, the form is passed to the get_queryset_and_facets method, which respectively returns a tuple of two elements: the QuerySet and the facets dictionary. Then, the current request object and the QuerySet is passed to the get_page method, which returns the current page object. Lastly, we create a context dictionary and render the response. ``` ## 還有更多 As you see, the get, post, and get_page methods are quite generic, so that we could create a generic class, FilterableListView, with those methods in the utils app. Then in any app, which needs a filterable list, we could create a view that extends FilterableListView and defines only the form_class and template_name attributes and the get_queryset_and_facets method. This is how class-based views work. ## 參閱 The Filtering object lists recipe The Managing paginated lists recipe ## 生成PDF文檔 Django views allow you to create much more than just HTML pages. You can generate files of any type. For example, you can create PDF documents for invoices, tickets, booking confirmations, or some other purposes. In this recipe, we will show you how to generate resumes (curriculum vitae) in PDF format out of the data from the database. We will be using the Pisa xhtml2pdf library, which is very practical as it allows you to use HTML templates to make PDF documents. ## 預熱 First of all, we need to install the Python libraries reportlab and xhtml2pdf in your virtual environment: ```python (myproject_env)$ pip install reportlab==2.4 (myproject_env)$ pip install xhtml2pdf ``` Then, let us create a cv app with a simple CV model with the Experience model attached to it through a foreign key. The CV model will have these fields: first name, last name, and e-mail. The Experience model will have these fields: the start date at a job, the end date at a job, company, position at that company, and skills gained: ```python #cv/models.py # -*- coding: UTF-8 -*- from django.db import models from django.utils.translation import ugettext_lazy as _ class CV(models.Model): first_name = models.CharField(_("First name"), max_length=40) last_name = models.CharField(_("Last name"), max_length=40) email = models.EmailField(_("Email")) def __unicode__(self): return self.first_name + " " + self.last_name class Experience(models.Model): cv = models.ForeignKey(CV) from_date = models.DateField(_("From")) till_date = models.DateField(_("Till"), null=True, blank=True) company = models.CharField(_("Company"), max_length=100) position = models.CharField(_("Position"), max_length=100) skills = models.TextField(_("Skills gained"), blank=True) def __unicode__(self): till = _("present") if self.till_date: till = self.till_date.strftime('%m/%Y') return _("%(from)s-%(till)s %(position)s at %(company)s") % { 'from': self.from_date.strftime('%m/%Y'), 'till': till,'position': self.position, 'company': self.company, } class Meta: ordering = ("-from_date",) ``` ## 工作原理 In the URL rules, let us create a rule for the view to download a PDF document of a resume by the ID of the CV model, as follows: ```python #cv/urls.py # -*- coding: UTF-8 -*- from django.conf.urls import patterns, url urlpatterns = patterns('cv.views', url(r'^(?P<cv_id>\d+)/pdf/$', 'download_cv_pdf', name='download_cv_pdf'), ) ``` Now let us create the download_cv_pdf view. This view renders an HTML template and then passes it to the PDF creator pisaDocument: ```python #cv/views.py # -*- coding: UTF-8 -*- try: from cStringIO import StringIO except ImportError: from StringIO import StringIO from xhtml2pdf import pisa from django.conf import settings from django.shortcuts import get_object_or_404 from django.template.loader import render_to_string from django.http import HttpResponse from cv.models import CV def download_cv_pdf(request, cv_id): cv = get_object_or_404(CV, pk=cv_id) response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; ' \ 'filename=%s_%s.pdf' % (cv.first_name, cv.last_name) html = render_to_string("cv/cv_pdf.html", { 'cv': cv, 'MEDIA_ROOT': settings.MEDIA_ROOT, 'STATIC_ROOT': settings.STATIC_ROOT, }) pdf = pisa.pisaDocument( StringIO(html.encode("UTF-8")), response, encoding='UTF-8', ) return response ``` At last, we create the template by which the document will be rendered, as follows: ``` {#templates/cv/cv_pdf.html#} <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title>My Title</title> <style type="text/css"> @page { size: "A4"; margin: 2.5cm 1.5cm 2.5cm 1.5cm; @frame footer { -pdf-frame-content: footerContent; bottom: 0cm; margin-left: 0cm; margin-right: 0cm; height: 1cm; } } #footerContent { color: #666; font-size: 10pt; text-align: center; } /* ... Other CSS Rules go here ... */ </style> </head> <body> <div> <p class="h1">Curriculum Vitae</p> <table> <tr> <td><p><b>{{ cv.first_name }} {{ cv.last_name }}</b><br /> Contact: {{ cv.email }}</p> </td> <td align="right"> <img src="{{ STATIC_ROOT }}/site/img/smiley.jpg" width="100" height="100" /> </td> </tr> </table> <p class="h2">Experience</p> <table> {% for experience in cv.experience_set.all %} <tr> <td><p>{{ experience.from_date|date:"F Y" }} - {% if experience.till_date %} {{ experience.till_date|date:"F Y" }} {% else %} present {% endif %}<br /> {{ experience.position }} at {{ experience.company }}</p> </td> <td><p><b>Skills gained</b><br> {{ experience.skills|linebreaksbr }} <br> <br> </p> </td> </tr> {% endfor %} </table> </div> <pdf:nextpage> <div> This is an empty page to make a paper plane. </div> <div id="footerContent"> Document generated at {% now "Y-m-d" %} | Page <pdf:pagenumber> of <pdf:pagecount> </div> </body> </html> ``` ## 工作原理 Depending on the data entered into the database, the rendered PDF document might look like this: 圖片:略 How does the view work? At first, we load a curriculum vitae by its ID if it exists, or raise the Page-not-found error if it does not. Then, we create the response object with mimetype of the PDF document. We set the Content-Disposition header to attachment with the specified filename. This will force browsers to open a dialog box prompting to save the PDF document and will suggest the specified name for the file. Then, we render the HTML template as a string passing curriculum vitae object, and the MEDIA_ROOT and STATIC_ROOT paths. >#####提示 Note that the src attribute of the <img> tag used for PDF creation needs to point to the file in the filesystem or the full URL of the image online. Then, we create a pisaDocument file with the UTF-8 encoded HTML as source, and response object as the destination. The response object is a file-like object, and pisaDocument writes the content of the document to it. The response object is returned by the view as expected. Let us have a look at the HTML template used to create this document. The template has some unusual HTML tags and CSS rules. If we want to have some elements on each page of the document, we can create CSS frames for that. In the preceding example, the <div> tag with the footerContent ID is marked as a frame, which will be repeated at the bottom of each page. In a similar way, we can have a header or a background image for each page. Here are the specific HTML tags used in this document: ``` The <pdf:nextpage> tag sets a manual page break The <pdf:pagenumber> tag returns the number of the current page The <pdf:pagecount> tag returns the total number of pages ``` The current version 3.0.33 of the Pisa xhtml2pdf library does not fully support all HTML tags and CSS rules; for example, <h1>, <h2>, and other headings are broken and there is no concept of floating, so instead you have to use paragraphs with CSS classes for different font styles and tables for multi-column layouts. However, this library is still mighty enough for customized layouts, which basically can be created just with the knowledge of HTML and CSS. ## 參閱 The Managing paginated lists recipe ********** 本章完 {% endraw %}
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看