Update database docs for plugin authors for new alembic branching world
[mediagoblin.git] / docs / source / pluginwriter / database.rst
1 .. MediaGoblin Documentation
2
3 Written in 2013 by MediaGoblin contributors
4
5 To the extent possible under law, the author(s) have dedicated all
6 copyright and related and neighboring rights to this software to
7 the public domain worldwide. This software is distributed without
8 any warranty.
9
10 You should have received a copy of the CC0 Public Domain
11 Dedication along with this software. If not, see
12 <http://creativecommons.org/publicdomain/zero/1.0/>.
13
14
15 .. _plugin-database-chapter:
16
17
18 ===========================
19 Database models for plugins
20 ===========================
21
22
23 Accessing Existing Data
24 =======================
25
26 If your plugin wants to access existing data, this is quite
27 straight forward. Just import the appropiate models and use
28 the full power of SQLAlchemy. Take a look at the (upcoming)
29 database section in the Developer's Chapter.
30
31
32 Creating new Tables
33 ===================
34
35 If your plugin needs some new space to store data, you
36 should create a new table. Please do not modify core
37 tables. Not doing so might seem inefficient and possibly
38 is. It will help keep things sane and easier to upgrade
39 versions later.
40
41 So if you create a new plugin and need new tables, create a
42 file named ``models.py`` in your plugin directory. You
43 might take a look at the core's db.models for some ideas.
44 Here's a simple one:
45
46 .. code-block:: python
47
48 from mediagoblin.db.base import Base
49 from sqlalchemy import Column, Integer, Unicode, ForeignKey
50
51 class MediaSecurity(Base):
52 __tablename__ = "yourplugin__media_security"
53
54 # The primary key *and* reference to the main media_entry
55 media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
56 primary_key=True)
57 get_media_entry = relationship("MediaEntry",
58 backref=backref("security_rating", cascade="all, delete-orphan"))
59
60 rating = Column(Unicode)
61
62 MODELS = [MediaSecurity]
63
64 Next, you need to make an initial migration. MediaGoblin uses
65 `Alembic's branching model <http://alembic.readthedocs.org/en/latest/branches.html>`_
66 to handle plugins adding their own content. As such, when you are
67 adding a new plugin, you need to add an initial migration adding
68 the existing models, and migrate from there.
69
70 You'll need to make a `migrations` subdirectory for migrations and put
71 your migrations there. If you want to look at some example
72 migrations, look at `mediagoblin/media_types/image/migrations/`,
73 especially the "initial" migration. (Plugin authors with plugins that
74 existed prior to the alembic switchover: you might notice how it
75 checks for the table and skips the migration if it already exists.
76 Plugin authors writing brand new plugins, post-Alembic migration
77 switchover, do not need to do this.)
78
79 Unfortunately, these migrations are a bit tedious to write.
80 Fortunately, Alembic can do much of the work for us! After adding the
81 models.py file, run this command (switching out YOUR_PLUGIN_NAME of
82 course)::
83
84 ./bin/gmg alembic --with-plugins revision \
85 --splice --autogenerate \
86 --branch-label YOUR_PLUGIN_NAME_plugin \
87 -m "YOUR_PLUGIN_NAME plugin initial migration"
88
89 (Note that `--with-plugins` *must* come before any alembic subcommand...
90 this is a quirk related to the way we handle alembic command dispatching
91 with the gmg subcommand!)
92
93 This will dump your migration into "the wrong place" (it'll dump it
94 into the MediaGoblin core migrations directory), so you should move it
95 to your plugin's migrations directory. Open the file and make adjustments
96 accordingly!
97
98 Some notes:
99
100 * Make sure all your ``__tablename__`` start with your
101 plugin's name so the tables of various plugins can't
102 conflict in the database. (Conflicts in python naming are
103 much easier to fix later).
104 * Try to get your database design as good as possible in
105 the first attempt. Changing the database design later,
106 when people already have data using the old design, is
107 possible (see next chapter), but it's not easy.
108
109
110 Changing the Database Schema Later
111 ==================================
112
113 If your plugin is in use and instances use it to store some data,
114 changing the database design is tricky and must be done with care,
115 but is not impossible.
116
117 Luckily, Alembic can once again help with autogenerating what is
118 probably very close to the migration you want. First you will need to
119 find out what the revision id of your plugin's most recent migrations
120 is. There are two ways to do this: look in your plugin's migrations/
121 directory and figure it out with the hope that it's "obvious" in some
122 way. The second path: let Alembic give that info for you.
123
124 Assuming you've already done the latest dbupdate with your plugin
125 enabled, do the following:
126
127 ./bin/gmg alembic --with-plugins heads
128
129 You should see the latest migration id for your plugin's label.
130
131 Make changes to your
132 plugin's models.py and then run::
133
134 ./bin/gmg alembic --with-plugins revision \
135 --head REVISION_HERE \
136 --autogenerate \
137 -m "YOUR_PLUGIN_NAME: Change explaination here."
138
139 Once again, this will dump the migration into the wrong place, so move
140 to your plugin's migrations directory. Open the file, adjust
141 accordingly, and read carefully! Now you should also test your
142 migration with some real data. Be sure to test it both on sqlite
143 *AND* on postgresql!
144
145 Whew, you made it! Get yourself a cookie to celebrate!