Sequelize.js: cara menggunakan migrasi dan sinkronisasi


142

Saya hampir memiliki proyek saya siap diluncurkan. Saya punya rencana besar setelah peluncuran dan struktur database akan berubah - kolom baru di tabel yang ada serta tabel baru, dan asosiasi baru ke model yang ada dan model baru.

Saya belum menyentuh migrasi di Sequelize, karena saya hanya memiliki data pengujian yang tidak keberatan saya hapus setiap kali database berubah.

Untuk itu, saat ini saya menjalankan sync force: trueaplikasi saya saat dijalankan, jika saya telah mengubah definisi model. Ini menghapus semua tabel dan membuatnya dari awal. Saya dapat menghilangkan forceopsi untuk membuatnya hanya membuat tabel baru. Tetapi jika sudah ada yang berubah hal ini tidak berguna.

Jadi, begitu saya menambahkan migrasi, bagaimana cara kerjanya? Jelas saya tidak ingin tabel yang ada (dengan data di dalamnya) dihapus, jadi sync force: trueitu tidak mungkin. Pada aplikasi lain yang saya bantu kembangkan (Laravel dan kerangka kerja lainnya) sebagai bagian dari prosedur penerapan aplikasi, kami menjalankan perintah migrasi untuk menjalankan migrasi yang tertunda. Namun dalam aplikasi ini, migrasi pertama memiliki basis data kerangka, dengan basis data dalam keadaan di mana pada beberapa waktu awal pengembangan - rilis alfa pertama atau apa pun. Jadi, bahkan instance aplikasi yang terlambat ke pesta bisa mendapatkan kecepatan sekaligus, dengan menjalankan semua migrasi secara berurutan.

Bagaimana cara menghasilkan "migrasi pertama" seperti itu di Sequelize? Jika saya tidak memilikinya, instance baru dari aplikasi entah bagaimana nantinya tidak akan memiliki database kerangka untuk menjalankan migrasi, atau akan menjalankan sinkronisasi di awal dan akan membuat database dalam status baru dengan semua tabel baru dll, tetapi kemudian ketika mencoba untuk menjalankan migrasi mereka tidak masuk akal, karena mereka ditulis dengan database asli dan setiap iterasi berturut-turut dalam pikiran.

Proses pemikiran saya: pada setiap tahap, database awal ditambah setiap migrasi secara berurutan harus sama (plus atau minus data) database yang dihasilkan ketika sync force: truedijalankan. Ini karena deskripsi model dalam kode menggambarkan struktur database. Jadi mungkin jika tidak ada tabel migrasi kita hanya menjalankan sinkronisasi dan menandai semua migrasi sebagai selesai, meskipun mereka tidak berjalan. Apakah ini yang perlu saya lakukan (bagaimana?), Atau Sequelize seharusnya melakukan ini sendiri, atau apakah saya menggonggong pohon yang salah? Dan jika saya berada di area yang tepat, tentunya seharusnya ada cara yang bagus untuk menghasilkan sebagian besar migrasi secara otomatis, mengingat model lama (dengan melakukan hash? Atau bahkan setiap migrasi dapat dikaitkan dengan komit? Saya akui, saya sedang berpikir dalam dunia git-sentris non-portabel) dan model baru. Ini dapat membedakan struktur dan menghasilkan perintah yang diperlukan untuk mengubah database dari lama ke baru, dan kembali, dan kemudian pengembang dapat masuk dan membuat perubahan yang diperlukan (menghapus / mentransisikan data tertentu dll).

Ketika saya menjalankan sekuel biner dengan --initperintah itu memberi saya direktori migrasi kosong. Ketika saya kemudian menjalankannya, sequelize --migrateitu membuat saya menjadi tabel SequelizeMeta tanpa apa pun di dalamnya, tidak ada tabel lain. Jelas tidak, karena biner itu tidak tahu cara mem-bootstrap aplikasi saya dan memuat model.

Saya pasti melewatkan sesuatu.

TLDR: bagaimana cara menyiapkan aplikasi saya dan migrasinya sehingga berbagai contoh aplikasi langsung dapat diperbarui, serta aplikasi baru tanpa database awal yang lama?


2
Saya telah menjawab terkait dengan alur kerja Anda, tetapi Idealnya semua Tabel harus diatur menggunakan Migrasi. Bahkan jika Anda menggunakan syncuntuk saat ini, idenya adalah bahwa migrasi "menghasilkan" seluruh database, jadi mengandalkan kerangka itu sendiri merupakan masalah. Alur kerja Ruby on Rails, misalnya, menggunakan Migrasi untuk semuanya, dan ini cukup mengagumkan setelah Anda terbiasa. Sunting: Dan ya, saya perhatikan pertanyaan ini cukup lama, tetapi karena belum pernah ada jawaban yang memuaskan dan orang-orang mungkin datang ke sini untuk mencari panduan, saya pikir saya harus berkontribusi.
Fernando Cordeiro

Jawaban:


89

Menghasilkan "migrasi pertama"

Dalam kasus Anda, cara paling andal adalah melakukannya hampir secara manual. Saya akan menyarankan untuk menggunakan alat sequelize-cli . Sintaksnya agak sederhana:

sequelize init
...
sequelize model:create --name User --attributes first_name:string,last_name:string,bio:text

Ini akan membuat model DAN migrasi. Kemudian, gabungkan model yang ada secara manual dengan generated with sequelize-cli, dan lakukan hal yang sama dengan migrasi. Setelah melakukan ini, hapus database (jika memungkinkan), dan jalankan

sequelize db:migrate

Ini akan membuat skema akan migrasi. Anda harus melakukan ini hanya sekali untuk beralih ke proses pengembangan skema yang tepat (tanpa sinkronisasi: kekuatan, tetapi dengan migrasi otoritatif).

Nanti, saat Anda perlu mengubah skema:

  1. Buat migrasi: sequelize migration:create
  2. Tulis fungsi atas dan bawah di file migrasi Anda
  3. Menurut perubahan Anda dalam file migrasi, ubah model Anda secara manual
  4. Lari sequelize db:migrate

Menjalankan migrasi pada produksi

Jelas Anda tidak dapat melakukan ssh ke server produksi dan menjalankan migrasi dengan tangan. Gunakan umzug , alat migrasi kerangka kerja agnostik untuk Node.JS untuk melakukan migrasi tertunda sebelum aplikasi dimulai.

Anda bisa mendapatkan daftar migrasi yang tertunda / belum dijalankan seperti ini:

umzug.pending().then(function (migrations) {
  // "migrations" will be an Array with the names of
  // pending migrations.
}); 

Kemudian jalankan migrasi ( di dalam callback ). Metode eksekusi adalah fungsi tujuan umum yang dijalankan untuk setiap migrasi yang ditentukan fungsi masing-masing:

umzug.execute({
  migrations: ['some-id', 'some-other-id'],
  method: 'up'
}).then(function (migrations) {
  // "migrations" will be an Array of all executed/reverted migrations.
});

Dan saran saya adalah melakukannya sebelum aplikasi dimulai dan mencoba melayani rute setiap saat. Sesuatu seperti ini:

umzug.pending().then(function(migrations) {
    // "migrations" will be an Array with the names of
    // pending migrations.
    umzug.execute({
        migrations: migrations,
        method: 'up'
    }).then(function(migrations) {
        // "migrations" will be an Array of all executed/reverted migrations.
        // start the server
        app.listen(3000);
        // do your stuff
    });
});

Saya tidak dapat mencoba ini sekarang, tetapi pada pandangan pertama seharusnya berhasil.

UPD April 2016

Setelah setahun, masih bermanfaat, sekian tips saya saat ini. Untuk saat ini, saya menginstal sequelize-clipaket sebagai ketergantungan langsung yang diperlukan , dan kemudian memodifikasi skrip startup NPM package.jsonseperti ini:

...
"scripts": {
  "dev": "grunt && sequelize db:migrate && sequelize db:seed:all && node bin/www",
  "start": "sequelize db:migrate && sequelize db:seed:all && node bin/www"
},
...

Satu-satunya hal yang perlu saya lakukan di server produksi adalah npm start. Perintah ini akan menjalankan semua migrasi, menerapkan semua seeder dan memulai server aplikasi. Tidak perlu memanggil umzug secara manual.


3
Ini terdengar seperti yang saya cari. Ini tidak tampak ajaib dan otomatis seperti yang "seharusnya", tapi mungkin inilah yang terbaik yang bisa diharapkan. Namun, saat ini saya tidak bekerja dengan Sequelize dan tidak akan dapat mengujinya dalam waktu dekat. Tetapi jika ada orang lain yang setuju bahwa solusi ini bagus, saya akan menerima jawaban ini. Saya masih merasa agak sedih karena tampaknya tidak ada cara untuk melakukan migrasi ini secara otomatis dari perbedaan antara versi model.
gemetar

4
@tremby kerangka satu-satunya yang saya gunakan yang benar-benar memahami model adalah Django. Ini menganalisis model dan bertanya seperti "Sepertinya Anda mengganti nama kolom menjadi first_name di model User. Apakah Anda ingin membuat migrasi untuk itu?" Di Django ia bekerja hampir secara ajaib, perkakas lain yang saya gunakan mengasumsikan pendekatan migrasi yang sama yang saya sebutkan di atas: Anda bertanggung jawab untuk menulis sendiri migrasi, memahami secara mendalam bidang jenis apa yang akan ditambahkan menjadi aktual ke status model Anda saat ini
f1nn

2
Anda dapat menyingkirkan pendingdan kemudian executemelakukannya umzug.up().then(function (migrations) { app.listen(3000); }). Menurut dokumentasi umzug, ini akan menjalankan semua migrasi yang tertunda.
Vinay

Saat Anda menyelesaikan migrasi, apakah menambahkan kolom ke skema di file model asli adalah hal yang umum?
theptrk

@ f1nn Saya memiliki pertanyaan tentang penyiapan Anda, bagaimana Anda menangani pengelompokan dan ketersediaan aplikasi? Saya akan mengintegrasikan pm2 dalam alur kerja saya dan mungkin itu tidak berfungsi langsung dengan skrip npm.
diosney

17

Baru saja mempelajarinya sendiri, tapi saya rasa saya akan merekomendasikan menggunakan migrasi sekarang agar Anda terbiasa dengannya. Saya telah menemukan hal terbaik untuk mencari tahu apa yang terjadi dalam migrasi adalah dengan melihat sql pada tabel yang dibuat oleh sequelize.sync()dan kemudian membangun migrasi dari sana.

migrations -c [migration name] 

Akan membuat file migrasi template di direktori migrasi. Anda kemudian dapat mengisinya dengan bidang yang perlu Anda buat. File ini perlu menyertakan createdAt/ updatedAt, bidang yang diperlukan untuk asosiasi, dll.

Untuk pembuatan tabel awal ke bawah harus memiliki:

migration.dropTable('MyTable');

Tetapi pembaruan berikutnya pada struktur tabel dapat mengabaikan ini dan hanya menggunakan tabel alter.

./node_modules/.bin/sequelize --migrate

Contoh pembuatan akan terlihat seperti:

module.exports = {
  up: function(migration, DataTypes, done) {
    migration.createTable(
        'MyTable',
        {
          id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
          },
          bigString: {type: DataTypes.TEXT, allowNull: false},
          MyOtherTableId: DataTypes.INTEGER,
          createdAt: {
            type: DataTypes.DATE
          },
          updatedAt: {
            type: DataTypes.DATE
          }
        });
    done();
  },
  down: function(migration, DataTypes, done) {
    migration.dropTable('MyTable');
    done();
  }

Untuk mengulang dari awal:

./node_modules/.bin/sequelize --migrate --undo
./node_modules/.bin/sequelize --migrate

Saya menggunakan kopi untuk menjalankan file benih untuk mengisi tabel setelah:

coffee server/seed.coffee

Ini hanya memiliki fungsi buat di dalamnya yang terlihat seperti:

user = db.User.create
  username: 'bob'
  password: 'suruncle'
  email: 'bob@bob.com'
.success (user) ->
  console.log 'added user'
  user_id = user.id
  myTable = [
    field1: 'womp'
    field2: 'rat'

    subModel: [
      field1: 'womp'
     ,
      field1: 'rat'
    ]
  ]

Ingatlah untuk sync()mengeluarkan indeks Anda dalam model Anda atau itu akan menimpa apa yang dilakukan migrasi dan seed.

Docs ada di http://sequelize.readthedocs.org/en/latest/docs/migrations/ tentunya. Tetapi jawaban dasarnya adalah Anda harus menambahkan semua yang ada dalam diri Anda untuk menentukan bidang yang Anda butuhkan. Itu tidak melakukannya untuk Anda.


5
Saya tidak menanyakan cara membuat dan menjalankan migrasi - seperti yang Anda tunjukkan, itu semua tersedia di dokumentasi. Apa yang saya tanyakan adalah bagaimana menggunakannya dalam konteks aplikasi yang dapat digunakan kembali di mana instance yang ada perlu diperbarui ke versi database yang lebih baru dan instance baru memerlukan database yang dibuat dari awal. Atau mungkin Anda sedang menjawab itu, dan mengatakan bahwa saya tidak harus menggunakan sync () sama sekali, dan membuat database awal dan semua perubahan dalam migrasi. Apa itu yang kamu katakan?
gemetar

1
@tremby Saya pikir itulah yang dia katakan. Anda dapat menggunakan sinkronisasi dan menangani hasilnya, atau Anda dapat membuat migrasi semuanya secara manual. Kerangka kerja kami, dengan gaya Rails-esque, menghasilkan file migrasi berdasarkan skema diff, saya akan SUKA jika Sequelize akan melakukannya untuk saya. Terlalu merepotkan untuk melakukan migrasi secara manual ...
diberdayakan

1
Sayang sekali Anda tidak dapat sequelize.sync()memiliki skrip yang dihasilkan yang membuat semua tabel dan indeks dasar sebagai migrasi pertama Anda (mirip dengan rails ' schema.rb.) Setelah membaca ini, tampaknya taruhan terbaik Anda adalah mengekspor skema awal Anda sebagai sql, lalu masukkan ke dalam execpernyataan besar di migrasi pertama Anda. Kemudian dari sana Anda menjalankan perubahan tambahan terhadap titik awal "versi 1.0" yang diketahui.
thom_nic

11

Untuk pengembangan , sekarang ada opsi untuk menyinkronkan tabel saat ini dengan mengubah strukturnya. Menggunakan versi terbaru dari repo github sekuel , Anda sekarang dapat menjalankan sinkronisasi dengan alterparameter.

Table.sync({alter: true})

Peringatan dari dokumen:

Mengubah tabel agar sesuai dengan model. Tidak direkomendasikan untuk penggunaan produksi. Menghapus data di kolom yang telah dihapus atau jenisnya berubah dalam model.


3

Sekarang dengan migrasi sekuel baru sangat sederhana.

Ini adalah contoh yang bisa Anda lakukan.

    'use strict';

    var Promise = require('bluebird'),
        fs = require('fs');

    module.exports = {
        up: function (queryInterface, Sequelize) {

            return Promise
                .resolve()
                .then(function() {
                    return fs.readFileSync(__dirname + '/../initial-db.sql', 'utf-8');
                })
                .then(function (initialSchema) {
                    return queryInterface.sequelize.query(initialSchema);
                })
        },

        down: function (queryInterface, Sequelize) {
            return Promise
                .resolve()
                .then(function() {
                    return fs.readFileSync(__dirname + '/../drop-initial-db.sql', 'utf-8');
                })
                .then(function (dropSql) {
                    return queryInterface.sequelize.query(dropSql);
                });
        }
    };

Ingat Anda harus mengatur:

"dialectOptions": { "multipleStatements": true }

pada konfigurasi database.


Bukankah ini hanya menjatuhkan dan membuat ulang database?
TWilly

Saya pikir menggunakan file sql besar awal bukanlah cara yang disarankan untuk melakukannya, karena ini akan mengikat adaptor dan database, yang jika tidak akan menjadi database agnostik, karena Anda dapat menggunakan untuk pengembangan sqlite dan untuk produksi mariadb atau lainnya.
diosney

3

Saya membaca posting ini dan pertanyaan serupa, itu tidak benar-benar menjawabnya untuk saya. Migrasi berguna untuk menjalankan database lokal dan untuk memperbarui data dalam produksi

Saya mengajukan pertanyaan di sini dan menjawabnya juga: Alur kerja untuk menangani migrasi dan inisialisasi sekuel?

Versi TL-DR untuk proyek greenfield

  1. Rancang skema database Anda seperti biasanya menggunakan skrip SQL murni atau jika Anda menggunakan alat gui
  2. Ketika Anda menyelesaikan semua 95% skema db Anda dan Anda puas dengannya, lanjutkan dan pindahkan ke sekuel dengan memindahkan seluruh .sqlfile ke atas
  3. Lakukan migrasi pertama Anda. Jalankan sequelize init:migratedi folder mana pun tempat Anda modelsberada
  4. Buat file migrasi pertama Anda. Larisequelize migration:generate --name [name_of_your_migration]
  5. Di file migrasi itu, letakkan kode ini di sana
("use strict");
/**
 * DROP SCHEMA public CASCADE; CREATE SCHEMA public
 * ^ there's a schema file with all the tables in there. it drops all of that, recreates
 */
const fs = require("fs");
const initialSqlScript = fs.readFileSync("./migrations/sql/Production001.sql", {
  encoding: "utf-8",
});
const db = require("../models");
module.exports = {
  up: () => db.sequelize.query(initialSqlScript),
  down: () =>
    db.sequelize.query(`DROP SCHEMA public CASCADE; CREATE SCHEMA public;
`),
};

masukkan deskripsi gambar di sini

dengan struktur folder umum ini

masukkan deskripsi gambar di sini

  1. Sekarang pengaturan sekuel Anda disinkronkan dengan skema database awal Anda
  2. ketika Anda ingin mengedit skema database Anda, jalankan ini lagi sequelize migration:generate --name [name_of_your_migration]
  3. Lanjutkan dan buat modifikasi Anda di sini di jalur migrasi updan down. Ini adalah pernyataan ALTER Anda untuk mengubah nama kolom, HAPUS, TAMBAHKAN kolom, dll
  4. Lari sequelize db:migrate
  5. Anda ingin model disinkronkan dengan perubahan pada db jarak jauh Anda, jadi yang dapat Anda lakukan sekarang adalah npm install sequelize-auto.
  6. Ini akan membaca skema database saat ini di database Anda dan otomatis menghasilkan file model. Gunakan perintah yang mirip dengan ini yang sequelize-auto -o "./models" -d sequelize_auto_test -h localhost -u my_username -p 5432 -x my_password -e postgresditemukan di https://github.com/sequelize/sequelize-auto

Anda dapat menggunakan git untuk melihat difflogs pada model Anda, seharusnya hanya ada perubahan yang mencerminkan perubahan dalam model database. Sebagai catatan tambahan, jangan pernah memodifikasi modelslangsung jika Anda menggunakan sequelize auto, karena ini akan membuatnya untuk Anda. Selain itu, Anda tidak lagi harus mengubah skema database Anda secara langsung dengan file SQL, asalkan ini adalah opsi karena Anda juga dapat mengimpor .sqlfile tersebut.

Sekarang skema database Anda sudah diperbarui, dan Anda secara resmi pindah ke sekuel migrasi database saja.

Semuanya dikontrol versi. Ini adalah alur kerja yang ideal untuk pengembang database dan backend


2

Gunakan versi. Versi aplikasi tergantung pada versi database. Jika versi baru memerlukan pembaruan database, buat migrasi untuk itu.

update: Saya memutuskan untuk meninggalkan migrasi ( KISS ) dan menjalankan skrip update_db (sync forse: false) bila diperlukan.


Mirip dengan tanggapan saya terhadap jawaban user1916988, apakah Anda mengatakan saya tidak boleh menggunakan sync()sama sekali, dan bahwa saya perlu menulis migrasi secara manual dari skema model versi lama ke model versi yang lebih baru?
gemetar

Saya memberi +1 karena pembaruan Anda. Saya benar-benar berpikir untuk melakukan hal yang sama. Menulis semua migrasi secara manual ketika aplikasi dapat melakukan itu agak bodoh, jadi saya hanya akan membuat skrip manual yang menjalankan aplikasi sekali dan menjalankan fungsi sinkronisasi.
Sallar

2

Agak terlambat, dan setelah membaca dokumentasi, Anda tidak perlu melakukan migrasi pertama yang Anda bicarakan. Yang harus Anda lakukan adalah memanggil syncuntuk membuat tabel.

sequelize.sync()

Anda juga dapat menjalankan sinkronisasi model sederhana dengan melakukan sesuatu seperti:

Project.sync()tetapi saya pikir itu sequelize.sync()adalah kasus umum yang lebih berguna untuk proyek Anda (selama Anda mengimpor model yang baik pada waktu mulai).

(diambil dari http://sequelizejs.com/docs/latest/models#database-synchronization )

Ini akan membuat semua struktur awal . Setelah itu, Anda hanya perlu membuat migrasi untuk mengembangkan skema Anda.

semoga membantu.


8
Saya tidak berpikir Anda membaca posting asli dengan sangat menyeluruh, atau mungkin saya tidak cukup jelas. Saya lebih dari sadar sequelize.sync()dan apa yang dilakukannya.
gemetar

2

Sequelize dapat menjalankan SQL sewenang-wenang secara asinkron .

Yang akan saya lakukan adalah:

  • Hasilkan Migrasi (Untuk digunakan sebagai migrasi pertama);
  • Hapus database Anda, seperti: mysql_dump -uUSER -pPASS DBNAME > FILE.SQL
  • Tempelkan dump lengkap sebagai teks (Berbahaya) atau muat File dengan dump lengkap di Node:
    • var baseSQL = "LOTS OF SQL and it's EVIL because you gotta put \ backslashes before line breakes and \"quotes\" and/or sum" + " one string for each line, or everything will break";
    • var baseSQL = fs.readFileSync('../seed/baseDump.sql');
  • Jalankan dump ini di Sequelize Migration:
module.exports = {
  up: function (migration, DataTypes) {
    var baseSQL = "whatever" // I recommend loading a file
    migration.migrator.sequelize.query(baseSQL);
  }
}

Itu harus mengatur pengaturan database, meskipun hal asinkron mungkin menjadi masalah. Jika itu terjadi, saya akan mencari cara untuk menunda mengembalikan upfungsi sequelize sampai queryfungsi async selesai.

Lebih lanjut tentang mysql_dump: http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html
Lebih lanjut tentang Sequelize Migration: http://sequelize.readthedocs.org/en/latest/docs/migrations/
Selengkapnya tentang Menjalankan SQL dari dalam Sequelize Migration: https://github.com/sequelize/sequelize/issues/313


1

Berikut adalah alur kerja saya saat ini. Saya terbuka untuk saran.

  1. Atur sequelize untuk membuat tabel yang tidak ada
  2. Atur sequelize untuk melepaskan dan membuat ulang semua tabel dalam database kosong bernama _blank
  3. Gunakan alat mysql untuk membandingkan _blank dan dan menyinkronkan perubahan menggunakan alat itu. Masih mencari alat terjangkau yang dapat melakukan ini di mac. Meja kerja MySql sepertinya Anda dapat mengimpor model dari skema yang sudah ada, lalu menyinkronkan skema. Mencoba mencari cara untuk melakukan ini melalui baris perintah untuk membuatnya mudah.

Dengan begitu Anda tidak perlu memperbarui tabel migrasi secara manual dan khawatir dengan jari gemuk, tetapi Anda tetap mendapatkan ORM.


1

Teman Saya memiliki pertanyaan yang sama dan berhasil memahami cara menggunakannya.

Saya mulai tanpa sekuel ORM karena itu saya sudah memiliki model data.
Saya harus membuat model secara otomatis dengan sequelize-auto dan menghasilkan migrasinya dengan file ini yang Anda buat https://gist.github.com/ahelord/a7a7d293695b71aadf04157f0f7dee64 dan disinkronkan ( {Force: false})
Ini ada di dev. Saya harus versi model dan migrasi dan menjalankannya setiap kali saya menarik kodenya.

Dalam produksi, server hanya di lantai atas sehingga Anda hanya perlu menjalankan migrasi dan di setiap pengelolaan komit karena Anda akan membuat versi model tanpa menghentikan backend


0

Bahkan ada cara yang lebih sederhana (menghindari Sequalize). Yang bunyinya seperti ini:

  1. Anda mengetik perintah di dalam proyek Anda: npm run migrate: new

  2. Ini menciptakan 3 file. File js, dan dua file sql bernama atas dan bawah

  3. Anda meletakkan pernyataan SQL Anda di file-file itu, yang merupakan sql murni
  4. Kemudian Anda mengetik: npm run migrate: up atau npm run migrate: down

Untuk bekerja, silakan lihat modul db-migrate .

Setelah Anda menyiapkannya (yang tidak sulit), mengubah DB Anda sangat mudah dan menghemat banyak waktu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.