Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
oleh Rick Anderson
Catatan
Versi terbaru dari tutorial ini tersedia di sini yang menggunakan ASP.NET MVC 5 dan Visual Studio 2013. Lebih aman, jauh lebih mudah untuk diikuti dan menunjukkan lebih banyak fitur.
Tutorial ini akan mengajari Anda dasar-dasar membangun aplikasi Web MVC ASP.NET menggunakan Microsoft Visual Web Developer 2010 Express Service Pack 1, yang merupakan versi gratis dari Microsoft Visual Studio. Sebelum memulai, pastikan Anda telah menginstal prasyarat yang tercantum di bawah ini. Anda dapat menginstal semuanya dengan mengklik tautan berikut: Penginstal Platform Web. Atau, Anda dapat menginstal prasyarat secara individual menggunakan tautan berikut:
- Prasyarat Visual Studio Web Developer Express SP1
- ASP.NET Pembaruan Alat MVC 3
- SQL Server Compact 4.0(runtime + dukungan alat)
Jika Anda menggunakan Visual Studio 2010 alih-alih Visual Web Developer 2010, instal prasyarat dengan mengklik tautan berikut: Prasyarat Visual Studio 2010.
Proyek Visual Web Developer dengan kode sumber C# tersedia untuk menyertai topik ini. Unduh versi C#. Jika Anda lebih suka Visual Basic, beralihlah ke versi Visual Basic dari tutorial ini.
Di bagian ini, Anda akan memeriksa metode tindakan dan tampilan yang dihasilkan untuk pengontrol film. Kemudian Anda akan menambahkan halaman pencarian kustom.
Jalankan aplikasi dan telusuri pengontrol Movies
dengan menambahkan /Movies ke URL di bilah alamat browser Anda. Tahan penunjuk mouse di atas tautan Edit untuk melihat URL yang ditautkannya.
Tautan Edit dihasilkan oleh Html.ActionLink
metode dalam tampilan Views\Movies\Index.cshtml :
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
Objek Html
adalah pembantu yang diekspos menggunakan properti di WebViewPage
kelas dasar. Metode ActionLink
pembantu memudahkan untuk secara dinamis menghasilkan hyperlink HTML yang ditautkan ke metode tindakan pada pengontrol. Argumen pertama untuk ActionLink
metode ini adalah teks tautan untuk dirender (misalnya, <a>Edit Me</a>
). Argumen kedua adalah nama metode tindakan yang akan dipanggil. Argumen akhir adalah objek anonim yang menghasilkan data rute (dalam hal ini, ID 4).
Tautan yang dihasilkan yang ditampilkan pada gambar sebelumnya adalah http://localhost:xxxxx/Movies/Edit/4
. Rute default mengambil pola {controller}/{action}/{id}
URL . Oleh karena itu, ASP.NET diterjemahkan http://localhost:xxxxx/Movies/Edit/4
ke dalam permintaan ke Edit
metode Movies
tindakan pengontrol dengan parameter ID
sama dengan 4.
Anda juga dapat meneruskan parameter metode tindakan menggunakan string kueri. Misalnya, URL http://localhost:xxxxx/Movies/Edit?ID=4
juga meneruskan parameter ID
4 ke Edit
metode Movies
tindakan pengontrol.
Movies
Buka pengontrol. Dua Edit
metode tindakan ditunjukkan di bawah ini.
//
// GET: /Movies/Edit/5
public ActionResult Edit(int id)
{
Movie movie = db.Movies.Find(id);
return View(movie);
}
//
// POST: /Movies/Edit/5
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Perhatikan metode tindakan kedua Edit
didahului oleh HttpPost
atribut . Atribut ini menentukan bahwa kelebihan beban Edit
metode hanya dapat dipanggil untuk permintaan POST. Anda dapat menerapkan atribut ke HttpGet
metode edit pertama, tetapi itu tidak perlu karena itu adalah default. (Kami akan merujuk ke metode tindakan yang secara implisit ditetapkan HttpGet
atribut sebagai HttpGet
metode.)
Metode mengambil HttpGet
Edit
parameter ID film, mencari film menggunakan metode Kerangka Kerja Find
Entitas, dan mengembalikan film yang dipilih ke tampilan Edit. Ketika sistem perancah membuat tampilan Edit, sistem memeriksa Movie
kelas dan membuat kode untuk merender <label>
dan <input>
elemen untuk setiap properti kelas. Contoh berikut menunjukkan tampilan Edit yang dihasilkan:
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
@Html.HiddenFor(model => model.ID)
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Genre)
@Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Perhatikan bagaimana templat tampilan memiliki @model MvcMovie.Models.Movie
pernyataan di bagian atas file — ini menentukan bahwa tampilan mengharapkan model untuk templat tampilan berjenis Movie
.
Kode perancah menggunakan beberapa metode pembantu untuk menyederhanakan markup HTML. Pembantu Html.LabelFor
menampilkan nama bidang ("Judul", "ReleaseDate", "Genre", atau "Harga"). Pembantu Html.EditorFor
menampilkan elemen HTML <input>
. Pembantu menampilkan pesan validasi apa pun yang terkait dengan properti tersebut Html.ValidationMessageFor
.
Jalankan aplikasi dan navigasikan ke URL /Movies . Klik tautan Edit. Di browser, lihat sumber untuk halaman tersebut. HTML di halaman terlihat seperti contoh berikut. (Markup menu dikecualikan untuk kejelasan.)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Edit</title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
</head>
<body>
<div class="page">
<header>
<div id="title">
<h1>MVC Movie App</h1>
</div>
...
</header>
<section id="main">
<h2>Edit</h2>
<script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
<form action="/Movies/Edit/4" method="post"> <fieldset>
<legend>Movie</legend>
<input data-val="true" data-val-number="The field ID must be a number."
data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />
<div class="editor-label">
<label for="Title">Title</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" />
<span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="ReleaseDate">ReleaseDate</label>
</div>
<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-required="The ReleaseDate field is required."
id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" />
<span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="Genre">Genre</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" />
<span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="Price">Price</label>
</div>
<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number."
data-val-required="The Price field is required." id="Price" name="Price" type="text" value="9.99" />
<span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
<div>
<a href="/Movies">Back to List</a>
</div>
</section>
<footer>
</footer>
</div>
</body>
</html>
Elemen <input>
berada dalam elemen HTML <form>
yang atributnya action
diatur untuk memposting ke URL /Movies/Edit . Data formulir akan diposting ke server saat tombol Edit diklik.
Memproses Permintaan POST
Daftar berikut menunjukkan HttpPost
versi Edit
metode tindakan.
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Pengikat model kerangka kerja ASP.NET mengambil nilai formulir yang diposting dan membuat Movie
objek yang diteruskan sebagai movie
parameter . Pemeriksaan ModelState.IsValid
kode memverifikasi bahwa data yang dikirimkan dalam formulir dapat digunakan untuk mengubah Movie
objek. Jika data valid, kode akan menyimpan data film ke Movies
pengumpulan MovieDBContext
instans. Kode kemudian menyimpan data film baru ke database dengan memanggil SaveChanges
metode MovieDBContext
, yang terus berubah ke database. Setelah menyimpan data, kode mengalihkan pengguna ke Index
metode MoviesController
tindakan kelas, yang menyebabkan film yang diperbarui ditampilkan dalam daftar film.
Jika nilai yang diposting tidak valid, nilai tersebut akan diputar ulang dalam formulir. Pembantu Html.ValidationMessageFor
dalam templat tampilan Edit.cshtml mengurus menampilkan pesan kesalahan yang sesuai.
Catatan tentang lokal Jika Anda biasanya bekerja dengan lokal selain bahasa Inggris, lihat Mendukung Validasi MVC 3 ASP.NET dengan Lokal Non-Bahasa Inggris.
Membuat Metode Edit Lebih Kuat
Metode HttpGet
Edit
yang dihasilkan oleh sistem perancah tidak memeriksa apakah ID yang diteruskan ke itu valid. Jika pengguna menghapus segmen ID dari URL (http://localhost:xxxxx/Movies/Edit
), kesalahan berikut ditampilkan:
Pengguna juga dapat meneruskan ID yang tidak ada di database, seperti http://localhost:xxxxx/Movies/Edit/1234
. Anda dapat membuat dua perubahan pada HttpGet
Edit
metode tindakan untuk mengatasi batasan ini. Pertama, ubah ID
parameter agar memiliki nilai default nol saat ID tidak diteruskan secara eksplisit. Anda juga dapat memeriksa apakah metode benar-benar Find
menemukan film sebelum mengembalikan objek film ke templat tampilan. Metode yang diperbarui Edit
ditunjukkan di bawah ini.
public ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
Jika tidak ada film yang ditemukan, HttpNotFound
metode dipanggil.
HttpGet
Semua metode mengikuti pola yang sama. Mereka mendapatkan objek film (atau daftar objek, dalam kasus Index
), dan meneruskan model ke tampilan. Metode Create
meneruskan objek film kosong ke tampilan Buat. Semua metode yang membuat, mengedit, menghapus, atau memodifikasi data melakukannya dalam HttpPost
kelebihan beban metode. Memodifikasi data dalam metode HTTP GET adalah risiko keamanan. Memodifikasi data dalam metode GET juga melanggar praktik terbaik HTTP dan pola REST arsitektur, yang menentukan bahwa permintaan GET tidak boleh mengubah status aplikasi Anda. Dengan kata lain, melakukan operasi GET harus menjadi operasi aman yang tidak memiliki efek samping.
Menambahkan Metode Pencarian dan Tampilan Pencarian
Di bagian ini Anda akan menambahkan SearchIndex
metode tindakan yang memungkinkan Anda mencari film berdasarkan genre atau nama. Ini akan tersedia menggunakan URL /Movies/SearchIndex . Permintaan akan menampilkan formulir HTML yang berisi elemen input yang dapat diisi pengguna untuk mencari film. Saat pengguna mengirimkan formulir, metode tindakan akan mendapatkan nilai pencarian yang diposting oleh pengguna dan menggunakan nilai untuk mencari database.
Menampilkan Formulir SearchIndex
Mulailah dengan menambahkan SearchIndex
metode tindakan ke kelas yang MoviesController
ada. Metode ini akan mengembalikan tampilan yang berisi formulir HTML. Berikut kodenya:
public ActionResult SearchIndex(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Baris SearchIndex
pertama metode membuat kueri LINQ berikut untuk memilih film:
var movies = from m in db.Movies
select m;
Kueri didefinisikan pada saat ini, tetapi belum dijalankan terhadap penyimpanan data.
searchString
Jika parameter berisi string, kueri film dimodifikasi untuk memfilter nilai string pencarian, menggunakan kode berikut:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
Kueri LINQ tidak dijalankan ketika didefinisikan atau ketika dimodifikasi dengan memanggil metode seperti Where
atau OrderBy
. Sebaliknya, eksekusi kueri ditangguhkan, yang berarti bahwa evaluasi ekspresi tertunda sampai nilai yang direalisasikan benar-benar diulang atau ToList
metode dipanggil. SearchIndex
Dalam sampel, kueri dijalankan dalam tampilan SearchIndex. Untuk informasi selengkapnya tentang eksekusi kueri yang ditangguhkan, lihat Eksekusi Kueri.
Sekarang Anda dapat menerapkan SearchIndex
tampilan yang akan menampilkan formulir kepada pengguna. Klik kanan di SearchIndex
dalam metode lalu klik Tambahkan Tampilan. Dalam kotak dialog Tambahkan Tampilan , tentukan bahwa Anda akan meneruskan Movie
objek ke templat tampilan sebagai kelas modelnya. Di daftar Templat perancah, pilih Daftar, lalu klik Tambahkan.
Saat Anda mengklik tombol Tambahkan , templat tampilan Views\Movies\SearchIndex.cshtml dibuat. Karena Anda memilih Daftar dalam daftar templat Perancah, Pengembang Web Visual secara otomatis membuat (perancah) beberapa konten default dalam tampilan. Perancah membuat formulir HTML. Ini memeriksa Movie
kelas dan membuat kode untuk merender <label>
elemen untuk setiap properti kelas. Daftar di bawah ini memperlihatkan tampilan Buat yang dihasilkan:
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "SearchIndex";
}
<h2>SearchIndex</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Title
</th>
<th>
ReleaseDate
</th>
<th>
Genre
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
@Html.ActionLink("Details", "Details", new { id=item.ID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
Jalankan aplikasi dan navigasikan ke /Movies/SearchIndex. Tambahkan string kueri seperti ?searchString=ghost
ke URL. Film yang difilter ditampilkan.
Jika Anda mengubah tanda tangan SearchIndex
metode agar memiliki parameter bernama id
, id
parameter akan cocok dengan {id}
tempat penampung untuk rute default yang diatur dalam file Global.asax .
{controller}/{action}/{id}
Metode yang dimodifikasi SearchIndex
akan terlihat sebagai berikut:
public ActionResult SearchIndex(string id)
{
string searchString = id;
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Sekarang Anda dapat meneruskan judul pencarian sebagai data rute (segmen URL) alih-alih sebagai nilai string kueri.
Namun, Anda tidak dapat mengharapkan pengguna untuk memodifikasi URL setiap kali mereka ingin mencari film. Jadi sekarang Anda akan menambahkan UI untuk membantu mereka memfilter film. Jika Anda mengubah tanda tangan SearchIndex
metode untuk menguji cara melewati parameter ID yang terikat rute, ubah kembali sehingga metode Anda SearchIndex
mengambil parameter string bernama searchString
:
public ActionResult SearchIndex(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Buka file Views\Movies\SearchIndex.cshtml, dan tepat setelah @Html.ActionLink("Create New", "Create")
, tambahkan yang berikut ini:
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" /></p>
}
Contoh berikut menunjukkan sebagian file Views\Movies\SearchIndex.cshtml dengan markup pemfilteran yang ditambahkan.
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "SearchIndex";
}
<h2>SearchIndex</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
</p>
Pembantu Html.BeginForm
membuat tag pembuka <form>
. Pembantu Html.BeginForm
menyebabkan formulir diposting ke dirinya sendiri ketika pengguna mengirimkan formulir dengan mengklik tombol Filter .
Jalankan aplikasi dan coba cari film.
Tidak ada HttpPost
kelebihan metode SearchIndex
. Anda tidak memerlukannya, karena metode tidak mengubah status aplikasi, hanya memfilter data.
Anda dapat menambahkan metode berikut HttpPost SearchIndex
. Dalam hal ini, pemanggil tindakan akan cocok dengan HttpPost SearchIndex
metode , dan HttpPost SearchIndex
metode akan berjalan seperti yang ditunjukkan pada gambar di bawah ini.
[HttpPost]
public string SearchIndex(FormCollection fc, string searchString)
{
return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>";
}
Namun, bahkan jika Anda menambahkan versi SearchIndex
metode iniHttpPost
, ada batasan dalam bagaimana semua ini telah diimplementasikan. Bayangkan Anda ingin menandai pencarian tertentu atau Anda ingin mengirim tautan ke teman yang dapat mereka klik untuk melihat daftar film yang difilter yang sama. Perhatikan bahwa URL untuk permintaan HTTP POST sama dengan URL untuk permintaan GET (localhost:xxxxx/Movies/SearchIndex) -- tidak ada informasi pencarian di URL itu sendiri. Saat ini, informasi string pencarian dikirim ke server sebagai nilai bidang formulir. Ini berarti Anda tidak dapat mengambil informasi pencarian tersebut untuk menandai atau mengirim ke teman di URL.
Solusinya adalah menggunakan kelebihan beban BeginForm
yang menentukan bahwa permintaan POST harus menambahkan informasi pencarian ke URL dan itu harus dirutekan ke versi HttpGet metode SearchIndex
. Ganti metode tanpa BeginForm
parameter yang ada dengan yang berikut ini:
@using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))
Sekarang saat Anda mengirimkan pencarian, URL berisi string kueri pencarian. Pencarian juga akan masuk ke HttpGet SearchIndex
metode tindakan, bahkan jika Anda memiliki HttpPost SearchIndex
metode .
Menambahkan Pencarian menurut Genre
Jika Anda menambahkan HttpPost
versi SearchIndex
metode , hapus sekarang.
Selanjutnya, Anda akan menambahkan fitur untuk memungkinkan pengguna mencari film berdasarkan genre. Ganti metode SearchIndex
dengan kode berikut:
public ActionResult SearchIndex(string movieGenre, string searchString)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
}
Versi SearchIndex
metode ini mengambil parameter tambahan, yaitu movieGenre
. Beberapa baris kode pertama membuat List
objek untuk menahan genre film dari database.
Kode berikut adalah kueri LINQ yang mengambil semua genre dari database.
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
Kode menggunakan AddRange
metode koleksi generik List
untuk menambahkan semua genre yang berbeda ke daftar. (Tanpa pengubah Distinct
, genre duplikat akan ditambahkan — misalnya, komedi akan ditambahkan dua kali dalam sampel kami). Kode kemudian menyimpan daftar genre dalam ViewBag
objek.
Kode berikut menunjukkan cara memeriksa movieGenre
parameter. Jika tidak kosong, kode akan membatasi kueri film lebih lanjut untuk membatasi film yang dipilih ke genre yang ditentukan.
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
Menambahkan Markup ke Tampilan SearchIndex untuk Mendukung Pencarian berdasarkan Genre
Html.DropDownList
Tambahkan pembantu ke file Views\Movies\SearchIndex.cshtml, tepat sebelum pembantuTextBox
. Markup yang telah selesai ditunjukkan di bawah ini:
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p>Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" /></p>
}
</p>
Jalankan aplikasi dan telusuri ke /Movies/SearchIndex. Coba cari berdasarkan genre, berdasarkan nama film, dan menurut kedua kriteria.
Di bagian ini Anda memeriksa metode tindakan CRUD dan tampilan yang dihasilkan oleh kerangka kerja. Anda membuat metode tindakan pencarian dan tampilan yang memungkinkan pengguna mencari berdasarkan judul dan genre film. Di bagian berikutnya, Anda akan melihat cara menambahkan properti ke Movie
model dan cara menambahkan penginisialisasi yang akan secara otomatis membuat database pengujian.