時間:2023-06-07 09:15:02 | 來源:網(wǎng)站運營
時間:2023-06-07 09:15:02 來源:網(wǎng)站運營
Django2.0創(chuàng)建電影評分網(wǎng)站(1):git checkout 0.1.0
即可查看本章完整代碼Django
項目Django App
創(chuàng)建我們的第一個模型,視圖,和模板requirements.dev.txt
,并寫入pip install -r requeirements.dev.txt
。django-admin startproject config
mkdir django
mv config django
tree .
,顯示如下:Django
大家常見的是用sqlite
,但現(xiàn)在postgresql
才是王道,所以我們也用它。DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }}# 將其更改為DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'mymdb', 'USER': 'mymdb', 'PASSWORD': 'development', 'HOST': '127.0.0.1', 'PORT': '5432', }}
$ cd django$ python manage.py startapp core
django/config/seetings.py
在INSTALLED_APPS
下面添加:INSTALLED_APPS = [ 'core', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles',]
django/core/models.py
后添加:from django.db import modelsclass Movie(models.Model): NOT_RATED = 0 RATED_G = 1 RATED_PG = 2 RATED_R = 3 RATINGS = ( (NOT_RATED, 'NR - Not Rated'), (RATED_G, 'G - General Audiences'), (RATED_PG, 'PG - Parental Guidance ' 'Suggested'), (RATED_R, 'R - Restricted'), ) title = models.CharField( max_length=140) plot = models.TextField() year = models.PositiveIntegerField() rating = models.IntegerField( choices=RATINGS, default=NOT_RATED) runtime = / models.PositiveIntegerField() website = models.URLField( blank=True) def __str__(self): return '{} ({})'.format( self.title, self.year)
django模型是一個從Model派生出來的具有一個或多個的字段的類。在數(shù)據(jù)庫中,model類類似于數(shù)據(jù)庫,F(xiàn)ield類對應于列,Model的實例對應于行。Django特有的ORM可以讓我們直接使用django來操作數(shù)據(jù)庫,而不是去寫sql。varchar
列,長度為140.需要你填入最大長度text
列,通常無最大長度integer
列,django將在保存之前檢查其是否為0或者更高VaildationError
。如果創(chuàng)建模型時未提供默認參數(shù),則默認參數(shù)會提供默認值。varchar(200)
。URLField還附帶驗證,檢查其值是否為有效的網(wǎng)絡(http / https / ftp / ftps
)URL。 admin應用程序使用空白參數(shù)來知道是否需要一個值(它不會影響數(shù)據(jù)庫)。__str__
模塊是幫助Django將模型轉換為字符串的方法。CREATE DATABASE mymdb;CREATE USER mymdb;GRANT ALL ON DATABASE mymdb to "mymdb";ALTER USER mymdb PASSWORD 'development';ALTER USER mymdb CREATEDB;
上述SQL語句將為我們的Django項目創(chuàng)建數(shù)據(jù)庫和用戶。 GRANT語句確保我們的mymdb用戶可以訪問數(shù)據(jù)庫。 然后,我們在mymdb用戶上設置密碼(確保它與settings.py文件中的密碼相同)。 最后,我們授予mymdb用戶創(chuàng)建新數(shù)據(jù)庫的權限,Django將在運行測試時使用這些數(shù)據(jù)庫創(chuàng)建測試數(shù)據(jù)庫。$ cd django$ python manage.py makemigrations coreMigrations for 'core': core/migrations/0001_initial.py - Create model Movie$ python manage.py migrate core Operations to perform: Apply all migrations: coreRunning migrations: Applying core.0001_initial... OK$ python manage.py dbshellpsql (9.6.1, server 9.6.3)Type "help" for help.mymdb=> /dt List of relations Schema | Name | Type | Owner --------+-------------------+-------+------- public | core_movie | table | mymdb public | django_migrations | table | mymdb(2 rows)mymdb=> /q$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, core, sessionsRunning migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying sessions.0001_initial... OK
$ cd django$ python manage.py shellPython 3.4.6 (default, Aug 4 2017, 15:21:32) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwinType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>> from core.models import Movie>>> sleuth = Movie.objects.create(... title='Sleuth',... plot='An snobbish writer who loves games'... ' invites his wife/'s lover for a battle of wits.',... year=1972,... runtime=138,... )>>> sleuth.id1>>> sleuth.get_rating_display()'NR - Not Rated'
objects
是模型的默認管理器。管理員是查詢模型表格的界面。它還提供了用于創(chuàng)建和保存實例的create()方法。每個模型必須至少有一個管理者,并且Django提供了一個默認管理。通常建議創(chuàng)建一個自定義管理器; 我們稍后會在添加人員和模型關系部分中看到。id
是主鍵,Django自動創(chuàng)建get_rating_display()
是Django添加的一種方法,因為評分字段有一個選擇元組。 我們不必在我們的create()調(diào)用中提供評分,因為評分字段具有默認值(0)。get_rating_display()方法查找該值并返回相應的顯示值。Django將為每個Field屬性生成一個像這樣的方法,并帶有一個choices參數(shù)。django/core/admin.py
from django.contrib import adminfrom core.models import Movieadmin.site.register(Movie)
然后執(zhí)行$ cd django$ python manage.py createsuperuser Username (leave blank to use 'tomaratyn'): 這里填你想注冊的名字Email address: 這里填郵箱Password: 密碼Password (again): 密碼Superuser created successfully.$ python manage.py runserverPerforming system checks...System check identified no issues (0 silenced).September 12, 2017 - 20:31:54Django version 1.11.5, using settings 'config.settings'Starting development server at http://127.0.0.1:8000/Quit the server with CONTROL-C.
打開瀏覽器進入http://127.0.0.1:8000/
,你將看到如下圖django/core/views.py
中from django.views.generic import ListViewfrom core.models import Movieclass MovieList(ListView): model = Movie
ListView
至少需要一個模型屬性。它會查詢模型中所有行,將其傳遞給模板,并在響應中返回呈現(xiàn)的模板。它還提供一些鉤子,我們可以用它來代替默認行為,這些行為已經(jīng)完全被記錄。ListView
是如何知道去查詢在Movie
中所有對象? 每個模型都有一個默認管理器。Manager類主要用于通過提供返回QuerySet
的方法(如all()
)查詢對象。ListView檢查它是否具有模板屬性,如果它存在,就知道模型類具有默認管理器,并且調(diào)用了all()
方法。ListView
還為我們提供了模板放置位置的慣例。如下:<app_name>/<model_name>_list.html
django/core/templates/core/movie_list.html
來寫第一個模板:<!DOCTYPE html><html> <body> <ul> {% for movie in object_list %} <li>{{ movie }}</li> {% empty %} <li> No movies yet. </li> {% endfor %} </ul> <p> Using https? {{ request.is_secure|yesno }} </p> </body></html>
在這里查看詳細的python模板知識。django/config/urls.py
,我們在我們的每個app中也創(chuàng)建一個路由,django/core/urls.py
,輸入:from django.urls import pathfrom . import viewsapp_name = 'core'urlpatterns = [ path('movies', views.MovieList.as_view(), name='MovieList'),]
最基本的,URLConf
是一個帶有urlpatterns
屬性的模塊,它是一個路徑列表。路徑由一個字符串組成,該字符串描述一個字符串,描述所討論的路徑和可調(diào)用的路徑。CBV不可調(diào)用,所以基類View
有一個返回可調(diào)用的靜態(tài)as_view()
方法。FBV可以作為回調(diào)傳入(沒有()運算符,它會執(zhí)行它們)。URLConf
到根目錄的URLConf
,更改django/config/urls.py
.from django.urls import path, includefrom django.contrib import adminimport core.urlsurlpatterns = [ path('admin/', admin.site.urls), path('', include( core.urls, namespace='core')),]
$ cd django$ python manage.py runserver
在瀏覽器中輸入,http://127.0.0.1:8000/movies
MovieDetail
視圖movie_detail.html
模板django/core/views.py
下寫入:from django.views.generic import ( ListView, DetailView,)from core.models import Movieclass MovieDetail(DetailView): model = Movieclass MovieList(ListView): model = Movie
DetailView
要求path()
對象在路徑字符串中包含pk
或slug
,以便DetailView
可以將該值傳遞給QuerySet
以查詢特定的模型實例。 一個slu
是一個簡短的URL
友好標簽,經(jīng)常用于內(nèi)容較重的網(wǎng)站,因為它是SEO
友好的。$ mkdir django/templates
在settings.py中更改:TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { # omittted for brevity }, },]
Django
中對模板的查找是從BASED_DIR
開始遞歸查找每一個名字是templates
的文件夾下的HTML
文件。django/templates/base.html
中寫入:<!DOCTYPE html><html lang="en" ><head > <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" > <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" rel="stylesheet" crossorigin="anonymous" > <title > {% block title %}MyMDB{% endblock %} </title> <style> .mymdb-masthead { background-color: #EEEEEE; margin-bottom: 1em; } </style></head ><body ><div class="mymdb-masthead"> <div class="container"> <nav class="nav"> <div class="navbar-brand">MyMDB</div> <a class="nav-link" href="{% url 'core:MovieList' %}" > Movies </a> </nav> </div></div><div class="container"> <div class="row"> <div class="col-sm-8 mymdb-main"> {% block main %}{% endblock %} </div> <div class="col-sm-3 offset-sm-1 mymdb-sidebar" > {% block sidebar %}{% endblock %} </div> </div></div></body ></html >
大多數(shù)HTML實際上是bootstrap的樣板,但我們在其中添加了一些python標記。{%block title%} MyMDB {%endblock%}
:這會創(chuàng)建一個其他模板可以替換的塊。 如果該塊未被替換,則將使用來自父模板的內(nèi)容。 href =“{% url 'core:MovieList' %}“
:url標記將為指定的路徑生成一個URL路徑。 URL
名稱應該被引用為<app_namespace>:<name>
; 在我們的例子中,core
是核心應用程序的名稱空間(per django/core/urls.py
),MovieList
是MovieList
視圖的URL
的名稱。django/core/templates/core/movie_detail.html
中添加:{% extends 'base.html' %}{% block title %} {{ object.title }} - {{ block.super }}{% endblock %}{% block main %}<h1>{{ object }}</h1><p class="lead">{{ object.plot }}</p>{% endblock %}{% block sidebar %}<div>This movie is rated: <span class="badge badge-primary"> {{ object.get_rating_display }} </span></div>{% endblock %}
這個模板中有很多HTML
,因為base.html
已經(jīng)有了。 所有MovieDetail.html
所要做的就是為base.html
定義的塊提供值。 我們來看看一些新的標簽:{%extends'base.html'%}
:如果一個模板想要擴展另一個模板,第一行必須是一個擴展標簽。 Django
將查找基本模板(可以擴展另一個模板)并首先執(zhí)行它,然后替換塊。 擴展另一個模板的模板不能包含塊以外的內(nèi)容,因為它不明確放置該內(nèi)容的位置。{{object.title}} - {{block.super}}
:我們引用block.super
在title
模板里面。block.super返回基本模板中標題模板塊的內(nèi)容。{{object.get_rating_display}}
:Django模板語言不使用()
來執(zhí)行該方法,只是通過名稱引用它將執(zhí)行該方法。from django.urls import pathfrom . import viewsurlpatterns = [ path('movies', views.MovieList.as_view(), name='MovieList'), path('movie/<int:pk>', views.MovieDetail.as_view(), name='MovieDetail'),]
python manage.py runserver
啟動,顯示如下{% extends 'base.html' %}{% block title %}All The Movies{% endblock %}{% block main %}<ul> {% for movie in object_list %} <li> <a href="{% url 'core:MovieDetail' pk=movie.id %}"> {{ movie }} </a> </li> {% endfor %} </ul>{% endblock %}
我們也看到url
標記與一個命名參數(shù)pk
一起使用,因為MovieDetail URL
需要一個pk
參數(shù)。 如果沒有提供參數(shù),那么Django
會在渲染時引發(fā)NoReverseMatch
異常,導致500
錯誤。class Movie(models.Model): # constants and fields omitted for brevity class Meta: ordering = ('-year', 'title') def __str__(self): return '{} ({})'.format( self.title, self.year)
排序為一個字符串列表或者元組,其是字段名稱組成,字符串可以選擇以-表示降序的字符作為前綴。('-year', 'title')
等價于 ORDER BY year DESC, title
.Meta
類添加排序將意味著來自模型管理器的QuerySets
將被排序。mian
模板部分下邊添加分頁{% block main %} <ul > {% for movie in object_list %} <li > <a href="{% url 'core:MovieDetail' pk=movie.id %}" > {{ movie }} </a > </li > {% endfor %} </ul > {% if is_paginated %} <nav > <ul class="pagination" > <li class="page-item" > <a href="{% url 'core:MovieList' %}?page=1" class="page-link" > First </a > </li > {% if page_obj.has_previous %} <li class="page-item" > <a href="{% url 'core:MovieList' %}?page={{ page_obj.previous_page_number }}" class="page-link" > {{ page_obj.previous_page_number }} </a > </li > {% endif %} <li class="page-item active" > <a href="{% url 'core:MovieList' %}?page={{ page_obj.number }}" class="page-link" > {{ page_obj.number }} </a > </li > {% if page_obj.has_next %} <li class="page-item" > <a href="{% url 'core:MovieList' %}?page={{ page_obj.next_page_number }}" class="page-link" > {{ page_obj.next_page_number }} </a > </li > {% endif %} <li class="page-item" > <a href="{% url 'core:MovieList' %}?page=last" class="page-link" > Last </a > </li > </ul > </nav > {% endif %}{% endblock %}
我們現(xiàn)在來看下其中終于的點:page_obj
是一個pag
類型,它知道關于這個頁面結果的信息。我們用它來檢查是否有使用has_next()/ has_previous()
的下一個/上一個頁面(我們不需要在Django模板語言中放置(),但has_next()
是一個方法而不是屬性)。我們也使用它來獲取next_page_number()/ previous_page_number()
。請注意,在檢索它們之前,使用has _ *()
方法檢查下一個/上一個頁碼的存在非常重要。 如果它們在檢索時不存在,則會引發(fā)EmptyPage
異常。object_list
繼續(xù)可用并保存正確的值。 盡管page_obj
在page_obj.object_list
中封裝了此頁面的結果,但ListView
確實可以繼續(xù)使用object_list
,并且我們的模板不會中斷。django/templates/404.html
{% extends "base.html" %}{% block title %}Not Found{% endblock %}{% block main %}<h1>Not Found</h1><p>Sorry that reel has gone missing.</p>{% endblock %}
目前,如果您有一個未使用的URL,例如http://localhost:8000/not-a-real-page
,您將看不到我們的自定義404模板,因為Django的DEBUG
設置在settings.py
中為True
。 要使我們的404模板可見,我們需要更改settings.py中的DEBUG
和ALLOWED_HOSTS
設置:DEBUG = FalseALLOWED_HOSTS = [ 'localhost', '127.0.0.1']
打開http://127.0.0.1:8000/not-a-real-page
,顯示如下:from django.test import TestCasefrom django.test.client import / RequestFactoryfrom django.urls.base import reversefrom core.models import Moviefrom core.views import MovieListclass MovieListPaginationTestCase(TestCase): ACTIVE_PAGINATION_HTML = """ <li class="page-item active"> <a href="{}?page={}" class="page-link">{}</a> </li> """ def setUp(self): for n in range(15): Movie.objects.create( title='Title {}'.format(n), year=1990 + n, runtime=100, ) def testFirstPage(self): movie_list_path = reverse('core:MovieList') request = RequestFactory().get(path=movie_list_path) response = MovieList.as_view()(request) self.assertEqual(200, response.status_code) self.assertTrue(response.context_data['is_paginated']) self.assertInHTML( self.ACTIVE_PAGINATION_HTML.format( movie_list_path, 1, 1), response.rendered_content)
我們來看看其中有意思的點:class MovieListPaginationTestCase(TestCase)
:TestCase
是所有Django的基礎測試集。它有許多內(nèi)置的便利,包括更多的斷言方法。def setUp(self)
像大多的測試框架一樣,Django的TestCase類也遵循測試啟動和測試結束。數(shù)據(jù)庫在每次測試之間進行清理,所以我們不必擔心刪除我們添加的任何模型。def testFirstPage(self)
:一個方法是一個測試,只要其前綴有test。Movie_list_path = reverse('core:MovieList')
:reverse()
是之前提到的,并且是Django模板標簽的Python等價物。 它會將名稱解析為路徑。request = RequestFactory().get(path = movie_list_path)
:RequestFactory是創(chuàng)建假HTTP請求的便利工廠。 一個RequestFactory有一些簡便的方法來創(chuàng)建GET,POST和PUT請求,方法是以動詞(例如GET請求的get())命名。 在我們的例子中,提供的路徑對象并不重要,但其他視圖可能需要檢查請求的路徑。$ cd django$ python manage.py test Creating test database for alias 'default'...System check identified no issues (0 silenced)..----------------------------------------------------------------------Ran 1 test in 0.035sOKDestroying test database for alias 'default'...
最后,我們已經(jīng)可以確信我們的分頁應用可以好好工作了。ForiengKey
字段的一對多關系,使用ManyToManyField
的多對多關系以及添加關于使用ManyToManyField
中的through
類實現(xiàn)多對多關系。Person
模型ForeignKey
字段用以從Movie
到Person
去追蹤導演ManyToManyField
從Movie
到Person
去追蹤作家ManyToManyField
和through
類去追蹤哪些人表演以及他們的關系PersonDetail
視圖添加到列表中,該視圖表示導演,作家,和表演者的class Person(models.Model): first_name = models.CharField( max_length=140) last_name = models.CharField( max_length=140) born = models.DateField() died = models.DateField(null=True, blank=True) class Meta: ordering = ( 'last_name', 'first_name') def __str__(self): if self.died: return '{}, {} ({}-{})'.format( self.last_name, self.first_name, self.born, self.died) return '{}, {} ({})'.format( self.last_name, self.first_name, self.born)
DateField
用于跟蹤基于日期的數(shù)據(jù),在數(shù)據(jù)庫中使用適當?shù)牧蓄愋停≒ostgres上的日期)和Python中的datetime.dat
e。 Django還提供了一個DateTimeField
來存儲日期和時間。null
參數(shù)(默認為False
),該參數(shù)指示列是否應接受NULL
SQL值(由Python中的None表示)。 我們標記死亡為支持NULL
,以便我們可以記錄人們的生活或死亡。 然后,在__str __()方法中,如果有人存活或死亡,我們會打印出不同的字符串表示形式。ForeignKey
字段,該字段將在兩個表之間創(chuàng)建具有外鍵(FK)約束(假設有數(shù)據(jù)庫支持)的列。在沒有ForeignKey
字段的模型中,Django會自動添加一個RelatedManager
對象作為實例屬性。 RelatedManager
類可以更輕松地查詢關系中的對象。我們將在下面的章節(jié)中看看這個例子。ManyToManyField()
; Django將為您創(chuàng)建一個RelatedManager
。如您所知,關系數(shù)據(jù)庫實際上不能在兩個表之間建立多對多的關系。相反,關系數(shù)據(jù)庫要求每個相關表都帶有外鍵的橋接表。假設我們不想添加任何描述關系的屬性,Django會自動為我們創(chuàng)建并管理這個橋接表。through
模型的ManyToManyField
(有時稱為UML / OO中的關聯(lián)類)。這個模型將有一個ForeignKey
到關系的每一邊以及我們想要的任何額外字段。class Movie(models.Model): # constants, methods, Meta class and other fields omitted for brevity. director = models.ForeignKey( to='Person', on_delete=models.SET_NULL, related_name='directed', null=True, blank=True)
to='Person
:Django的所有關系字段都可以接受字符串引用以及相關模型的引用。這個參數(shù)是必須的。on_delete=model.SET_NULL
:Django需要得到當引用的模型被刪掉時應該怎么做的指示。SET_NULL
將把被刪除的Person
指向的所有Movie
模型實例的director字段設置為NULL
。 如果我們想要級聯(lián)刪除,我們將使用models.CASCADE
對象。related_name='directed
,這是一個可選參數(shù),指示另一個模型上的RelatedManager實例的名稱。這可以讓我們查詢一個Person定向的所有Movie模型實例)null = True
或提供默認值。如果我們不這樣做,那么migrations
就會迫使我們?nèi)?。這個要求的存在是因為Django必須假定在遷移運行時表中存在行(即使沒有)。當數(shù)據(jù)庫添加新列時,它需要知道它應該插入到現(xiàn)有行中。在director字段的情況下,我們可以接受它有時可能為NULL。Movie
中,并將一個新屬性添加到Person
實例,名為directed
(RelatedManager
類型)。 RelatedManager
是一個非常有用的類,它類似于模型的默認管理器,但會自動管理兩個模型之間的關系。person.directed.create()
并將其與Movie.objects.create()
進行比較。兩種方法都會創(chuàng)建一個新電影,但是person.directed.create()
將確保新電影有人作為其導演。 RelatedManager
還提供了添加和刪除方法,以便我們可以通過調(diào)用person.directed.add(movie)
將電影添加到指定的Person
集。還有一個類似的remove()
方法,但從關系中刪除了一個模型。class Movie(models.Model): # constants, methods, Meta class and other fields omitted for brevity. writers = models.ManyToManyField( to='Person', related_name='writing_credits', blank=True)
ManyToManyField
建立了一個多對多的關系,并且像一個RelatedManager
一樣,允許用戶查詢和創(chuàng)建模型。 我們再次使用related_name
來避免給Person一個movie_set
屬性,而是給它一個將成為RelatedManager
的writing_credits
屬性。ManyToManyField
的情況下,關系的兩邊都有RelatedManager
,因此person.writing_credits.add(movie)
與movie.writers.add(person)
的效果相同。class Movie(models.Model): # constants, methods, Meta class and other fields omitted for brevity. actors = models.ManyToManyField( to='Person', through='Role', related_name='acting_credits', blank=True)class Role(models.Model): movie = models.ForeignKey(Movie, on_delete=models.DO_NOTHING) person = models.ForeignKey(Person, on_delete=models.DO_NOTHING) name = models.CharField(max_length=140) def __str__(self): return "{} {} {}".format(self.movie_id, self.person_id, self.name) class Meta: unique_together = ('movie', 'person', 'name')
這看起來像前面的ManyToManyField
,除了我們有一個to
(就像前面Person
)和一個through
(引用Role
)參數(shù)。Role
模型看起來很像設計一個連接表;它對多對多關系的每一方都有一個ForeignKey
。它還有一個名為name
的額外字段來描述角色。Meta
類的角色上設置unique_together
屬性將防止重復的數(shù)據(jù)。ManyToManyField
的這個用戶將創(chuàng)建四個新的RelatedManager
實例:movie.actors
將作為Person
的關聯(lián)管理person.acting_credits
將作為Movie
的關聯(lián)管理movie.role_set
將作為Role
的關聯(lián)管理person.role_set
將作為Role
的關聯(lián)管理role_set
管理者,以創(chuàng)建模型或修改中間類的關系。 如果您嘗試運行movie.actors.add(person)
,Django將拋出IntegrityError異常,因為無法填充Role.name
的值。 但是,您可以編寫movie.role_set.add(person = person,name ='Hamlet')
。$ python manage.py makemigrations coreMigrations for 'core': core/migrations/0002_auto_20170926_1650.py - Create model Person - Create model Role - Change Meta options on movie - Add field movie to role - Add field person to role - Add field actors to movie - Add field director to movie - Add field writers to movie - Alter unique_together for role (1 constraint(s))$ python manage.py migrate coreOperations to perform: Apply all migrations: coreRunning migrations: Applying core.0002_auto_20170926_1651... OK
movie_detail.html
模板可以鏈接到的PersonDetail
視圖。 為了創(chuàng)造我們的視圖,我們將通過四個步驟:PersonDetail
視圖將列出一個Person
正在表演,寫作或導演的所有電影。在我們的模板中,我們將打印出每部電影中的每部電影的名稱(以及演出的Role.name)。為了避免向數(shù)據(jù)庫發(fā)送大量查詢,我們將為我們的模型創(chuàng)建新的管理器,以返回更智能的QuerySet
。person.role_set.all()
,每個相關角色一個)。對于N部電影中的人員,這將導致對數(shù)據(jù)庫進行N次查詢。我們可以用prefetch_related()
方法避免這種情況(稍后我們將看看select_related()
方法)。使用prefetch_related()
方法,Django將在單個附加查詢中查詢單個關系中的所有相關數(shù)據(jù)。但是,如果我們不最終使用預取數(shù)據(jù),查詢它會浪費時間和內(nèi)存。all_with_prefetch_movies()
創(chuàng)建一個PersonManager,并使其成為Person的默認管理器:class PersonManager(models.Manager): def all_with_prefetch_movies(self): qs = self.get_queryset() return qs.prefetch_related( 'directed', 'writing_credits', 'role_set__movie')class Person(models.Model): # fields omitted for brevity objects = PersonManager() class Meta: ordering = ( 'last_name', 'first_name') def __str__(self): # body omitted for brevity
我們的PersonManager
仍然提供所有與默認相同的方法,因為PersonManager
繼承自models.Manager
。我們還定義了一個新方法,它使用get_queryset()
來獲取QuerySet
,并告訴它預取相關模型。 QuerySets
是懶惰的,所以直到查詢集被評估為止(例如,迭代,投射到布爾,切片或由if語句評估),才會與數(shù)據(jù)庫進行通信。在使用get()通過PK獲取模型之前,DetailView
不會評估查詢。prefetch_related()
方法接受一個或多個查找,并在初始查詢完成后自動查詢這些相關模型。當您從QuerySet訪問與某個模型相關的模型時,Django將不必查詢它,因為您已經(jīng)在QuerySet
中預取了它。RelatedManager
的內(nèi)容。通過用兩個下劃線分隔關系字段(或RelatedManager
)和相關模型字段的名稱,查找甚至可以跨越關系:Movie.objects.all().filteractors__last_name='Freeman', actors__first_name='Morgan')
前面的調(diào)用將返回一個查詢集,用于1Morgan Freeman1一直扮演演員的所有Movie
模型實例。PersonManager
中,我們告訴Django預取一個Person已經(jīng)執(zhí)行,寫入并且有角色的所有電影,以及預取它們自己的角色。 無論人員多么多產(chǎn),使用all_with_prefetch_movies()
方法都會導致不斷的查詢次數(shù)。django/core/views.py
class PersonDetail(DetailView): queryset = Person.objects.all_with_prefetch_movies()
這個DetailView
是不同的,因為我們沒有提供它的模型屬性。 相反,我們從PersonManager
類中為它提供一個QuerySet
對象。 當DetailView
使用QuerySet的filter()
和get()
方法來檢索模型實例時,DetailView
將從模型實例的類名中派生出模板的名稱,就像我們已經(jīng)提供了模型類作為視圖上的屬性一樣。django/core/templates/core/person_detail.html
寫入:{% extends 'base.html' %}{% block title %} {{ object.first_name }} {{ object.last_name }}{% endblock %}{% block main %} <h1>{{ object }}</h1> <h2>Actor</h2> <ul > {% for role in object.role_set.all %} <li > <a href="{% url 'core:MovieDetail' role.movie.id %}" > {{ role.movie }} </a >: {{ role.name }} </li > {% endfor %} </ul > <h2>Writer</h2> <ul > {% for movie in object.writing_credits.all %} <li > <a href="{% url 'core:MovieDetail' movie.id %}" > {{ movie }} </a > </li > {% endfor %} </ul > <h2>Director</h2> <ul > {% for movie in object.directed.all %} <li > <a href="{% url 'core:MovieDetail' movie.id %}" > {{ movie }} </a > </li > {% endfor %} </ul >{% endblock %}
class MovieManager(models.Manager): def all_with_related_persons(self): qs = self.get_queryset() qs = qs.select_related( 'director') qs = qs.prefetch_related( 'writers', 'actors') return qsclass Movie(models.Model): # constants and fields omitted for brevity objects = MovieManager() class Meta: ordering = ('-year', 'title') def __str__(self): # method body omitted for brevity
MovieManager
引入了另一種名為select_related()
的新方法。 select_related()
方法與prefetch_related()
方法非常相似,但在關系僅導致一個相關模型(例如,使用ForeignKey
字段)時使用。 select_related()
方法通過使用JOIN SQL查詢來檢索一個查詢中的兩個模型。 當關系可能導致多個模型(例如,ManyToManyField
或RelatedManager
屬性的任一側)時,使用prefetch_related()
。MovieDetail
視圖以直接使用查詢集而不是模型:class MovieDetail(DetailView): queryset = ( Movie.objects .all_with_related_persons())
該視圖完全相同,但每次需要相關的Person模型實例時都不需要查詢數(shù)據(jù)庫,因為它們都是預取的。關鍵詞:評分,電影,創(chuàng)建
微信公眾號
版權所有? 億企邦 1997-2025 保留一切法律許可權利。