如何查看代碼:clone代碼后git checkout 0.1.0即可查看本章完整代碼

項目介紹做一個自己的MDBDjango項目使用Django App創(chuàng)建我們的第一個模型,視圖,和" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > Django2.0創(chuàng)建電影評分網(wǎng)站(1)

Django2.0創(chuàng)建電影評分網(wǎng)站(1)

時間:2023-06-07 09:15:02 | 來源:網(wǎng)站運營

時間:2023-06-07 09:15:02 來源:網(wǎng)站運營

Django2.0創(chuàng)建電影評分網(wǎng)站(1):

Django初體驗(一個電影評分網(wǎng)站)

完整代碼

如何查看代碼:

clone代碼后git checkout 0.1.0即可查看本章完整代碼

項目介紹

快速開始

工程創(chuàng)建

創(chuàng)建一個文件夾
$ mkdir MyMDB
$ cd MyMDB

  1. 創(chuàng)建python虛擬環(huán)境,請自行查詢pipenv使用
    $ pip install pipenv
    $ pipenv --python 3.6
    $ pipenv shell
  2. 在MyMDB文件夾中創(chuàng)建一個文件,命名為requirements.dev.txt,并寫入
    django<2.1
    psycopg2<2.8
  3. 確認你已進入pipenv shell,執(zhí)行pip install -r requeirements.dev.txt。
  4. 創(chuàng)建Django工程 django-admin startproject config
  5. 在MyMDB目錄下:
    mkdir django mv config django
  6. 成功后執(zhí)行 tree .,顯示如下:
    ├── django
    │ ├── config
    │ │ ├── __init__.py
    │ │ ├── settings.py
    │ │ ├── urls.py
    │ │ └── wsgi.py
    │ ├── manage.py
    ├── Pipfile
    ├── Pipfile.lock
    ├── requestment.dev.txt

Django文件簡介

了解數(shù)據(jù)庫設置

使用Django大家常見的是用sqlite,但現(xiàn)在postgresql才是王道,所以我們也用它。

在settings.py中找到DATABASES,如:

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', }}

核心App

Django app是遵循 Model View Template(MTV) 模式的。我們需要知道的是:

Django并不限制我們的app數(shù)量,所以我們可以創(chuàng)建無數(shù)我們需要的app。

創(chuàng)建core app

確定進入pipenv shell環(huán)境后(下文均是如此)操作

$ cd django$ python manage.py startapp core

安裝app

找到django/config/seetings.pyINSTALLED_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。

__str__模塊是幫助Django將模型轉換為字符串的方法。

數(shù)據(jù)庫遷移

在postgresql中創(chuàng)建數(shù)據(jù)庫和用戶

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ù)庫。

然后執(zhí)行

$ 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

創(chuàng)建我們第一個電影

執(zhí)行

$ 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'

創(chuàng)建電影后臺管理

Django有一個后臺UI管理工具。 要讓Django的admin app和模型一起工作,要有如下步驟:

  1. 注冊模型
  2. 建立超級用戶登錄
  3. 允許開發(fā)服務
  4. 通過后臺登錄
現(xiàn)在我們在admin中注冊Movie模型,打開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/,你將看到如下圖







登錄后按下圖操作:

然后點擊右上角ADD MOVIE




創(chuàng)建電影列表視圖

django/core/views.py

from django.views.generic import ListViewfrom core.models import Movieclass MovieList(ListView): model = MovieListView至少需要一個模型屬性。它會查詢模型中所有行,將其傳遞給模板,并在響應中返回呈現(xiàn)的模板。它還提供一些鉤子,我們可以用它來代替默認行為,這些行為已經(jīng)完全被記錄。

ListView是如何知道去查詢在Movie中所有對象? 每個模型都有一個默認管理器。Manager類主要用于通過提供返回QuerySet的方法(如all())查詢對象。ListView檢查它是否具有模板屬性,如果它存在,就知道模型類具有默認管理器,并且調(diào)用了all()方法。ListView還為我們提供了模板放置位置的慣例。如下:<app_name>/<model_name>_list.html

添加我們的第一個模板(movie_list.html)

Django有自己的模板語言。但也可以使用其他的,例如Jinja2。 我們在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模板知識。

使用URLConf將路由請求返回到視圖

現(xiàn)在我們有了模型、視圖和模板,我們需要告訴Django哪一個請求將使用URLConf發(fā)送到我們的MovieList。每一個新項目都有一個根URLConf,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提供了一個reverse()函數(shù)和url模板標簽,用于從名稱到完整路徑到視圖。

app_name變量設置此URLConf所屬的應用程序。通過這種方式,我們可以引用一個沒有Django的命名路徑,讓其他具有相同名稱路徑的應用程序感到困惑(例如,index是一個非常常見的名稱,所以我們可以說appA:index和appB:index來區(qū)分它們) 。

最后,我們來連接我們的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')),]

運行開發(fā)環(huán)境

$ cd django$ python manage.py runserver在瀏覽器中輸入,http://127.0.0.1:8000/movies




看到上圖,我們的第一個部分成功了。

個人電影頁面

接下來快速創(chuàng)建新的電影信息頁面。會議創(chuàng)建core app的過程,我舉一反三:

  1. 創(chuàng)建一個MovieDetail視圖
  2. 創(chuàng)建movie_detail.html模板
  3. 添加視圖路由

創(chuàng)建Detail視圖

django/core/views.py下寫入:

from django.views.generic import ( ListView, DetailView,)from core.models import Movieclass MovieDetail(DetailView): model = Movieclass MovieList(ListView): model = MovieDetailView要求path()對象在路徑字符串中包含pkslug,以便DetailView可以將該值傳遞給QuerySet以查詢特定的模型實例。 一個slu是一個簡短的URL友好標簽,經(jīng)常用于內(nèi)容較重的網(wǎng)站,因為它是SEO友好的。

創(chuàng)建movie_detail.html模板

Django的模板語言支持模板繼承,這意味著您可以為網(wǎng)站編寫一個具有所有外觀的模板,并標記其他模板將覆蓋的塊部分。 這使我們可以創(chuàng)建整個網(wǎng)站的外觀,而無需編輯每個模板。 我們用它來創(chuàng)建一個包含MyMDB品牌和外觀的基本模板,然后添加一個從基本模板繼承的Movie Detail模板。

$ 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),MovieListMovieList視圖的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.supertitle模板里面。block.super返回基本模板中標題模板塊的內(nèi)容。

{{object.get_rating_display}}:Django模板語言不使用()來執(zhí)行該方法,只是通過名稱引用它將執(zhí)行該方法。

添加MovieDetail到core.urls.py

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啟動,顯示如下







分頁和鏈接電影列表到MovieDetail

更新MovieList.html去繼承base.html

{% 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錯誤。

啟動后如下圖







設置order

現(xiàn)在的觀點存在另一個問題,就是沒有沒有order,數(shù)據(jù)庫會返回一個無序查詢,那么分頁導航將無用。更重要的,不能保證用戶每次更改內(nèi)容后當前頁面會保持一致,因為數(shù)據(jù)庫會返回不同順序的結果。我們的查詢需要有序。

使模型有序更容易開發(fā)。無論是用debug,還是寫單元測試,或是使用shell,我們的模型都會以一致的順序返回,可以使故障排除變得更簡單。

一個Django模型可以選擇一個名為Meta的內(nèi)部類,它允許我們指定模型的信息,讓我們添加一個帶有排序屬性的Meta類。

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將被排序。

添加分頁

現(xiàn)在我們的電影總是以相同的方式排列,我們來添加分頁。 Django ListView已經(jīng)內(nèi)置了對分頁的支持,所以我們需要做的就是利用它。 分頁由GET參數(shù)頁面控制,該頁面控制顯示哪個頁面。

現(xiàn)在在我們的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)在來看下其中終于的點:

創(chuàng)建404頁面

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中的DEBUGALLOWED_HOSTS設置:

DEBUG = FalseALLOWED_HOSTS = [ 'localhost', '127.0.0.1']打開http://127.0.0.1:8000/not-a-real-page,顯示如下:







測試我們的視圖和模板

讓我們添加一個執(zhí)行以下功能的測試:




在test.py中寫入:

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)我們來看看其中有意思的點:

測試如下:

$ 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)可以確信我們的分頁應用可以好好工作了。

添加人員和模型關系

在這一部分,我們將要添加關系模型到我們的項目。人們與電影的關系可以創(chuàng)建一個復雜的數(shù)據(jù)模型。同一個人可以是演員,作家和導演(例如,The Apostle(1997)的編寫,導演,主演均是Robert Duvall)。即使忽略了人員與團隊并簡化一下,數(shù)據(jù)模型也將包含使用ForiengKey字段的一對多關系,使用ManyToManyField的多對多關系以及添加關于使用ManyToManyField中的through類實現(xiàn)多對多關系。

在者一部分,我們將如下一步步的操作:

  1. 創(chuàng)建Person模型
  2. 添加ForeignKey字段用以從MoviePerson去追蹤導演
  3. 添加ManyToManyFieldMoviePerson去追蹤作家
  4. 添加ManyToManyFieldthrough類去追蹤哪些人表演以及他們的關系
  5. 創(chuàng)建數(shù)據(jù)庫遷移
  6. 將導演,作者和演員添加到電影詳情模板
  7. 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.date。 Django還提供了一個DateTimeField來存儲日期和時間。

所有字段都支持null參數(shù)(默認為False),該參數(shù)指示列是否應接受NULLSQL值(由Python中的None表示)。 我們標記死亡為支持NULL,以便我們可以記錄人們的生活或死亡。 然后,在__str __()方法中,如果有人存活或死亡,我們會打印出不同的字符串表示形式。

不同類型的關系字段

Django的ORM支持使用中間模型映射模型之間關系的字段,包括一對多,多對多和多對多關系。

當兩個模型具有一對多關系時,我們使用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到關系的每一邊以及我們想要的任何額外字段。

我們將創(chuàng)建這些中的每一個的示例,因為我們將導演,作者和演員添加到我們的電影模型中。

導演--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)這也是我們第一次為現(xiàn)有模型添加一個字段。這樣做時,我們必須添加null = True或提供默認值。如果我們不這樣做,那么migrations就會迫使我們?nèi)?。這個要求的存在是因為Django必須假定在遷移運行時表中存在行(即使沒有)。當數(shù)據(jù)庫添加新列時,它需要知道它應該插入到現(xiàn)有行中。在director字段的情況下,我們可以接受它有時可能為NULL。

我們現(xiàn)在已經(jīng)將一個字段添加到Movie中,并將一個新屬性添加到Person實例,名為directedRelatedManager類型)。 RelatedManager是一個非常有用的類,它類似于模型的默認管理器,但會自動管理兩個模型之間的關系。

我們來看看person.directed.create()并將其與Movie.objects.create()進行比較。兩種方法都會創(chuàng)建一個新電影,但是person.directed.create()將確保新電影有人作為其導演。 RelatedManager還提供了添加和刪除方法,以便我們可以通過調(diào)用person.directed.add(movie)將電影添加到指定的Person集。還有一個類似的remove()方法,但從關系中刪除了一個模型。

作家--ManyToManyField

兩模塊也可能有多對多的關系,例如,一個人可能會寫很多電影,而一部電影可能會被很多人寫出。 接下來,我們將添加一個作家字段到我們的電影模型:

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屬性,而是給它一個將成為RelatedManagerwriting_credits屬性。

ManyToManyField的情況下,關系的兩邊都有RelatedManager,因此person.writing_credits.add(movie)movie.writers.add(person)的效果相同。

角色-ManyToManyField with a through class

當我們想要使用中介模型來描述兩個其他模型之間的關系時,我們會看到關系字段的最后一個例子,該模型具有多對多的關系。 Django讓我們通過創(chuàng)建一個模型來描述兩個模型之間的多對多關系中的連接表。

在我們的案例中,我們將通過角色創(chuàng)建Movie和Person之間的多對多關系,該關系將具有name屬性:

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實例:

我們可以使用任何管理者查詢模型,但只能查看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

創(chuàng)建人員視圖,更新電影列表

我們添加一個我們的movie_detail.html模板可以鏈接到的PersonDetail視圖。 為了創(chuàng)造我們的視圖,我們將通過四個步驟:

  1. 創(chuàng)建一個管理器來限制數(shù)據(jù)庫查詢的數(shù)量
  2. 創(chuàng)建我們的視圖
  3. 創(chuàng)建我們的模板
  4. 創(chuàng)建一個引用我們視圖的URL

創(chuàng)建一個自定義管理器--PersonManager

我們的PersonDetail視圖將列出一個Person正在表演,寫作或導演的所有電影。在我們的模板中,我們將打印出每部電影中的每部電影的名稱(以及演出的Role.name)。為了避免向數(shù)據(jù)庫發(fā)送大量查詢,我們將為我們的模型創(chuàng)建新的管理器,以返回更智能的QuerySet。

在Django中,只要我們在關系中訪問一個屬性,Django就會查詢數(shù)據(jù)庫以獲取相關的項目(在每個項目上循環(huán)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中預取了它。

查找是Django 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ù)。

創(chuàng)建一個PersonDetail視圖和模板

我們可以寫一個非常精簡的視圖在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 %}

創(chuàng)建MovieManager

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 brevityMovieManager引入了另一種名為select_related()的新方法。 select_related()方法與prefetch_related()方法非常相似,但在關系僅導致一個相關模型(例如,使用ForeignKey字段)時使用。 select_related()方法通過使用JOIN SQL查詢來檢索一個查詢中的兩個模型。 當關系可能導致多個模型(例如,ManyToManyFieldRelatedManager屬性的任一側)時,使用prefetch_related()。

現(xiàn)在,我們可以更新我們的MovieDetail視圖以直接使用查詢集而不是模型:

class MovieDetail(DetailView): queryset = ( Movie.objects .all_with_related_persons())該視圖完全相同,但每次需要相關的Person模型實例時都不需要查詢數(shù)據(jù)庫,因為它們都是預取的。

總結

在本節(jié)中,我們創(chuàng)建了我們的Django項目并開始了我們的核心Django應用程序。我們看到了如何使用Django的模型 - 視圖 - 模板方法來創(chuàng)建易于理解的代碼。我們根據(jù)Django龐大模型,簡潔視圖和沉默模板的最佳實踐創(chuàng)建了模型附近的集中數(shù)據(jù)庫邏輯,視圖中的分頁和模板中的HTML。

現(xiàn)在我們已經(jīng)準備好添加可以注冊并對自己喜歡的電影進行投票的用戶。

關鍵詞:評分,電影,創(chuàng)建

74
73
25
news

版權所有? 億企邦 1997-2025 保留一切法律許可權利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關閉