Added optional support for database migrations with south. The game/migrate.py program is a simple wrapper that runs the suitable commands for setting up a database and updating it, respectively.
This commit is contained in:
parent
7eaf3d221c
commit
7fb6362dc4
9 changed files with 153 additions and 30 deletions
|
|
@ -162,7 +162,6 @@ def stop_server(parser, options, args):
|
||||||
else:
|
else:
|
||||||
print '\n\rUnknown OS detected, can not stop. '
|
print '\n\rUnknown OS detected, can not stop. '
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
"""
|
||||||
Beginning of the program logic.
|
Beginning of the program logic.
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ class CmdListScripts(MuxCommand):
|
||||||
else:
|
else:
|
||||||
table[1].append(script.obj.key)
|
table[1].append(script.obj.key)
|
||||||
table[2].append(script.key)
|
table[2].append(script.key)
|
||||||
if not hasattr(script, 'interval') or not script.interval:
|
if not hasattr(script, 'interval') or script.interval < 0:
|
||||||
table[3].append("--")
|
table[3].append("--")
|
||||||
else:
|
else:
|
||||||
table[3].append("%ss" % script.interval)
|
table[3].append("%ss" % script.interval)
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,7 @@ from src.settings_default import *
|
||||||
|
|
||||||
print """
|
print """
|
||||||
Welcome to Evennia (version %s)!
|
Welcome to Evennia (version %s)!
|
||||||
Created a fresh settings.py file for you.""" % VERSION
|
We created a fresh settings.py file for you.""" % VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from game import settings
|
from game import settings
|
||||||
|
|
|
||||||
124
game/migrate.py
Executable file
124
game/migrate.py
Executable file
|
|
@ -0,0 +1,124 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
Database migration helper, using South.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
- Install South using the method suitable for your platform
|
||||||
|
http://south.aeracode.org/docs/installation.html
|
||||||
|
|
||||||
|
- You need to have a database setup, either an old one or
|
||||||
|
a fresh one. If the latter, run manage.py syncdb as normal,
|
||||||
|
entering superuser info etc.
|
||||||
|
|
||||||
|
- Start this tool and use the 'initialize' option. South will
|
||||||
|
create a migration scheme for all Evennia components.
|
||||||
|
|
||||||
|
That's all you need to do until Evennia's database scheme changes,
|
||||||
|
something which is usually announced with the update. To update
|
||||||
|
your current database automatically, follow these steps:
|
||||||
|
|
||||||
|
- Run this tool
|
||||||
|
|
||||||
|
- Select the Update option.
|
||||||
|
|
||||||
|
That is all. :)
|
||||||
|
|
||||||
|
For more advanced migrations, there might be further instructions.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
from subprocess import call
|
||||||
|
|
||||||
|
|
||||||
|
# Set the Python path up so we can get to settings.py from here.
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
|
||||||
|
|
||||||
|
if not os.path.exists('settings.py'):
|
||||||
|
# make sure we have a settings.py file.
|
||||||
|
print " No settings.py file found. Launching manage.py ..."
|
||||||
|
|
||||||
|
import game.manage
|
||||||
|
|
||||||
|
print """
|
||||||
|
Now configure Evennia by editing your new settings.py file.
|
||||||
|
If you haven't already, you should also create/configure the
|
||||||
|
database with 'python manage.py syncdb' before continuing."""
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Get the settings
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
# Prepare all valid apps
|
||||||
|
APPLIST = [app.split('.')[-1] for app in settings.INSTALLED_APPS
|
||||||
|
if app.startswith("src.") or app.startswith("game.")]
|
||||||
|
|
||||||
|
def run_south(mode):
|
||||||
|
"""
|
||||||
|
Simply call manage.py with the appropriate South commands.
|
||||||
|
"""
|
||||||
|
if mode == "init":
|
||||||
|
for appname in APPLIST:
|
||||||
|
print "Initializing %s ..." % appname
|
||||||
|
call(["python", "manage.py", "convert_to_south", appname])
|
||||||
|
print "\nInitialization complete. That's all you need to do for now."
|
||||||
|
elif mode == "update":
|
||||||
|
for appname in APPLIST:
|
||||||
|
print "Updating/migrating schema for %s ..." % appname
|
||||||
|
call(["python", "manage.py", "schemamigration", appname, "--auto"])
|
||||||
|
call(["python", "manage.py", "migrate", appname])
|
||||||
|
print "\nUpdate complete."
|
||||||
|
|
||||||
|
def south_ui():
|
||||||
|
"""
|
||||||
|
Simple menu for handling migrations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
string = """
|
||||||
|
Evennia Database Migration Tool
|
||||||
|
|
||||||
|
You usually don't need to use this tool unless a new version of Evennia tells you that
|
||||||
|
the database scheme changed in some way, AND you don't want to reset your database.
|
||||||
|
This tool will help you to migrate an existing database without having to manually edit
|
||||||
|
your tables and fields to match the new scheme. For that to work you must have run this
|
||||||
|
tool *before* applying the changes however.
|
||||||
|
|
||||||
|
This is a simple wrapper on top of South, a Django database scheme migration tool.
|
||||||
|
If you want more control, you can call manage.py directly using the instructions
|
||||||
|
found at http://south.aeracode.org/docs.
|
||||||
|
|
||||||
|
NOTE: Evennia is still in Alpha - there is no guarantee that database changes will still
|
||||||
|
not be too advanced to handle with this simple tool, and it is too soon to talk
|
||||||
|
of supplying custom migration schemes to new versions.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
i - Initialize an existing/fresh database with migration mappings (done once)
|
||||||
|
u - Update an initialized database to the changed scheme
|
||||||
|
q - Quit
|
||||||
|
"""
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print string
|
||||||
|
inp = str(raw_input(" Option > "))
|
||||||
|
inp = inp.lower()
|
||||||
|
if inp in ["q", "i", "u"]:
|
||||||
|
if inp == 'i':
|
||||||
|
run_south("init")
|
||||||
|
elif inp == 'u':
|
||||||
|
run_south("update")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
if not 'south' in settings.INSTALLED_APPS:
|
||||||
|
string = "\n The 'south' database migration tool does not seem to be installed."
|
||||||
|
string += "\n You can find it here: http://south.aeracide.org.\n"
|
||||||
|
print string
|
||||||
|
else:
|
||||||
|
south_ui()
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
# comma-separated list of alias-names of this object. Note that default
|
# comma-separated list of alias-names of this object. Note that default
|
||||||
# searches only search aliases in the same location as caller.
|
# searches only search aliases in the same location as caller.
|
||||||
db_aliases = models.ForeignKey(Alias, db_index=True, blank=True, null=True)
|
db_aliases = models.ForeignKey(Alias, blank=True, null=True, db_index=True)
|
||||||
# If this is a character object, the player is connected here.
|
# If this is a character object, the player is connected here.
|
||||||
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True)
|
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True)
|
||||||
# The location in the game world. Since this one is likely
|
# The location in the game world. Since this one is likely
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class ScriptHandler(object):
|
||||||
interval = "inf"
|
interval = "inf"
|
||||||
next_repeat = "inf"
|
next_repeat = "inf"
|
||||||
repeats = "inf"
|
repeats = "inf"
|
||||||
if script.interval:
|
if script.interval > 0:
|
||||||
interval = script.interval
|
interval = script.interval
|
||||||
if script.repeats:
|
if script.repeats:
|
||||||
repeats = script.repeats
|
repeats = script.repeats
|
||||||
|
|
|
||||||
|
|
@ -441,8 +441,8 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
# South handles automatic database scheme migrations when evennia updates
|
# South handles automatic database scheme migrations when evennia updates
|
||||||
#try:
|
try:
|
||||||
# import south
|
import south
|
||||||
# INSTALLED_APPS = INSTALLED_APPS + ('south',)
|
INSTALLED_APPS = INSTALLED_APPS + ('south',)
|
||||||
#except ImportError:
|
except ImportError:
|
||||||
# pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,17 @@ from django.db.models.base import Model, ModelBase
|
||||||
|
|
||||||
from manager import SharedMemoryManager
|
from manager import SharedMemoryManager
|
||||||
|
|
||||||
TCACHE = {}
|
TCACHE = {} # test cache, for debugging /Griatch
|
||||||
|
|
||||||
class SharedMemoryModelBase(ModelBase):
|
class SharedMemoryModelBase(ModelBase):
|
||||||
def __new__(cls, name, bases, attrs):
|
#def __new__(cls, name, bases, attrs):
|
||||||
super_new = super(ModelBase, cls).__new__
|
# super_new = super(ModelBase, cls).__new__
|
||||||
parents = [b for b in bases if isinstance(b, SharedMemoryModelBase)]
|
# parents = [b for b in bases if isinstance(b, SharedMemoryModelBase)]
|
||||||
if not parents:
|
# if not parents:
|
||||||
# If this isn't a subclass of Model, don't do anything special.
|
# # If this isn't a subclass of Model, don't do anything special.
|
||||||
return super_new(cls, name, bases, attrs)
|
# print "not a subclass of Model", name, bases
|
||||||
|
# return super_new(cls, name, bases, attrs)
|
||||||
return super(SharedMemoryModelBase, cls).__new__(cls, name, bases, attrs)
|
# return super(SharedMemoryModelBase, cls).__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
def __call__(cls, *args, **kwargs):
|
def __call__(cls, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -49,6 +49,9 @@ class SharedMemoryModel(Model):
|
||||||
# subclass now?
|
# subclass now?
|
||||||
__metaclass__ = SharedMemoryModelBase
|
__metaclass__ = SharedMemoryModelBase
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def _get_cache_key(cls, args, kwargs):
|
def _get_cache_key(cls, args, kwargs):
|
||||||
"""
|
"""
|
||||||
This method is used by the caching subsystem to infer the PK value from the constructor arguments.
|
This method is used by the caching subsystem to infer the PK value from the constructor arguments.
|
||||||
|
|
|
||||||
|
|
@ -21,22 +21,21 @@ def reload_modules():
|
||||||
"""
|
"""
|
||||||
Reload modules that don't have any variables that can be reset.
|
Reload modules that don't have any variables that can be reset.
|
||||||
Note that python reloading is a tricky art and strange things have
|
Note that python reloading is a tricky art and strange things have
|
||||||
been known to happen if debugging and reloading a lot while
|
been known to happen if debugging and reloading a lot. A server
|
||||||
working with src/ modules. A cold reboot is often needed
|
cold reboot is often needed eventually.
|
||||||
eventually.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# We protect e.g. src/ from reload since reloading it in a running
|
# We protect e.g. src/ from reload since reloading it in a running
|
||||||
# server can create unexpected results (and besides, we should
|
# server can create unexpected results (and besides, non-evennia devs
|
||||||
# never need to do that anyway. Updating src requires a server
|
# should never need to do that anyway). Updating src requires a server
|
||||||
# reboot).
|
# reboot.
|
||||||
protected_dirs = ('src.',)
|
protected_dirs = ('src.',)
|
||||||
|
|
||||||
# flag 'dangerous' typeclasses (those which retain a memory
|
# flag 'dangerous' typeclasses (those which retain a memory
|
||||||
# reference, notably Scripts with a timer component) for
|
# reference, notably Scripts with a timer component) for
|
||||||
# non-reload, since these cannot be safely cleaned from memory
|
# non-reload, since these cannot be safely cleaned from memory
|
||||||
# without causing havoc. A server reboot is required for updating
|
# without causing havoc. A server reboot is required for updating
|
||||||
# these.
|
# these (or killing all running, timed scripts).
|
||||||
unsafe_modules = []
|
unsafe_modules = []
|
||||||
for scriptobj in ScriptDB.objects.get_all_scripts():
|
for scriptobj in ScriptDB.objects.get_all_scripts():
|
||||||
if (scriptobj.interval > -1) and scriptobj.typeclass_path:
|
if (scriptobj.interval > -1) and scriptobj.typeclass_path:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue