From 6eff51de20f499f3a7a0331f44a6d0a0d0a8d5a0 Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 5 Apr 2011 23:28:40 +0000 Subject: [PATCH] OBS: Migration needed(objects, scripts, players). This resolves issue 136 by allowing database objects to be nested in lists and dictionaries as attributes on objects. Behind the scenes, only the DBREF is stored since storing dbobjects cannot be pickled. One used to be able to store single objects this way, but objects hidden in nested iterable structures were not found. Note that ONLY lists and dictionaries are supported to store on attributes - custom iterables will be stored and retrieved as a generic list instead - this is a tradeoff to be able to store database objects. To migrate, give the following commands from game/: migrate.py migrate objects migrate.py migrate scripts migrate.py migrate players --- game/gamesrc/objects/examples/red_button.py | 4 +- ...02_auto__del_field_objattribute_db_mode.py | 127 ++++++++++++++ src/players/manager.py | 4 +- ...auto__del_field_playerattribute_db_mode.py | 113 ++++++++++++ ...auto__del_field_scriptattribute_db_mode.py | 129 ++++++++++++++ src/typeclasses/models.py | 164 +++++++----------- 6 files changed, 435 insertions(+), 106 deletions(-) create mode 100644 src/objects/migrations/0002_auto__del_field_objattribute_db_mode.py create mode 100644 src/players/migrations/0002_auto__del_field_playerattribute_db_mode.py create mode 100644 src/scripts/migrations/0002_auto__del_field_scriptattribute_db_mode.py diff --git a/game/gamesrc/objects/examples/red_button.py b/game/gamesrc/objects/examples/red_button.py index fbb0d9b8ff..51d97f3c41 100644 --- a/game/gamesrc/objects/examples/red_button.py +++ b/game/gamesrc/objects/examples/red_button.py @@ -90,7 +90,7 @@ class RedButton(Object): # scripts that depend on the lid to be closed. self.scripts.validate() # now add new scripts that define the open-lid state - self.obj.scripts.add(scriptexamples.OpenLidState) + self.scripts.add(scriptexamples.OpenLidState) # we also add a scripted event that will close the lid after a while. # (this one cleans itself after being called once) self.scripts.add(scriptexamples.CloseLidEvent) @@ -125,7 +125,7 @@ class RedButton(Object): """ self.db.lamp_works = False - self.obj.db.desc = "The big red button has stopped blinking for the time being." + self.db.desc = "The big red button has stopped blinking for the time being." if feedback and self.location: string = "The lamp flickers, the button going dark." diff --git a/src/objects/migrations/0002_auto__del_field_objattribute_db_mode.py b/src/objects/migrations/0002_auto__del_field_objattribute_db_mode.py new file mode 100644 index 0000000000..f1be4dc3d2 --- /dev/null +++ b/src/objects/migrations/0002_auto__del_field_objattribute_db_mode.py @@ -0,0 +1,127 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'ObjAttribute.db_mode' + + from src.objects.models import ObjAttribute + from src.typeclasses.models import PackedDBobject + for attr in ObjAttribute.objects.all(): + # resave attributes + db_mode = attr.db_mode + if db_mode and db_mode != 'pickle': + # an object. We need to resave this. + if db_mode == 'object': + val = PackedDBobject(attr.db_value, "objectdb") + elif db_mode == 'player': + val = PackedDBobject(attr.db_value, "playerdb") + elif db_mode == 'script': + val = PackedDBobject(attr.db_value, "scriptdb") + elif db_mode == 'help': + val = PackedDBobject(attr.db_value, "helpentry") + else: + val = PackedDBobject(attr.db_value, db_mode) # channel, msg + attr.value = val + + db.delete_column('objects_objattribute', 'db_mode') + + + def backwards(self, orm): + + # Adding field 'ObjAttribute.db_mode' + db.add_column('objects_objattribute', 'db_mode', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'objects.alias': { + 'Meta': {'object_name': 'Alias'}, + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'objects.nick': { + 'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'Nick'}, + 'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}), + 'db_real': ('django.db.models.fields.TextField', [], {}), + 'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'objects.objattribute': { + 'Meta': {'object_name': 'ObjAttribute'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}), + 'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['objects'] diff --git a/src/players/manager.py b/src/players/manager.py index e164d6810f..5038c95325 100644 --- a/src/players/manager.py +++ b/src/players/manager.py @@ -5,7 +5,7 @@ The managers for the custom Player object and permissions. import datetime from django.db import models from django.contrib.auth.models import User -from src.typeclasses.managers import returns_typeclass_list, returns_typeclass +from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager from src.utils import logger # @@ -59,7 +59,7 @@ def returns_player(method): return None return func -class PlayerManager(models.Manager): +class PlayerManager(TypedObjectManager): """ Custom manager for the player profile model. We use this to wrap users in general in evennia, and supply some useful diff --git a/src/players/migrations/0002_auto__del_field_playerattribute_db_mode.py b/src/players/migrations/0002_auto__del_field_playerattribute_db_mode.py new file mode 100644 index 0000000000..3a30ec0bd8 --- /dev/null +++ b/src/players/migrations/0002_auto__del_field_playerattribute_db_mode.py @@ -0,0 +1,113 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'PlayerAttribute.db_mode' + + from src.players.models import PlayerAttribute + from src.typeclasses.models import PackedDBobject + for attr in PlayerAttribute.objects.all(): + # resave attributes + db_mode = attr.db_mode + if db_mode and db_mode != 'pickle': + # an object. We need to resave this. + if db_mode == 'object': + val = PackedDBobject(attr.db_value, "objectdb") + elif db_mode == 'player': + val = PackedDBobject(attr.db_value, "playerdb") + elif db_mode == 'script': + val = PackedDBobject(attr.db_value, "scriptdb") + elif db_mode == 'help': + val = PackedDBobject(attr.db_value, "helpentry") + else: + val = PackedDBobject(attr.db_value, db_mode) # channel, msg + attr.value = val + + db.delete_column('players_playerattribute', 'db_mode') + + + def backwards(self, orm): + + # Adding field 'PlayerAttribute.db_mode' + db.add_column('players_playerattribute', 'db_mode', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'players.playerattribute': { + 'Meta': {'object_name': 'PlayerAttribute'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}), + 'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['players'] diff --git a/src/scripts/migrations/0002_auto__del_field_scriptattribute_db_mode.py b/src/scripts/migrations/0002_auto__del_field_scriptattribute_db_mode.py new file mode 100644 index 0000000000..6777333c3f --- /dev/null +++ b/src/scripts/migrations/0002_auto__del_field_scriptattribute_db_mode.py @@ -0,0 +1,129 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'ScriptAttribute.db_mode' + + from src.scripts.models import ScriptAttribute + from src.typeclasses.models import PackedDBobject + for attr in ScriptAttribute.objects.all(): + # resave attributes + db_mode = attr.db_mode + if db_mode and db_mode != 'pickle': + # an object. We need to resave this. + if db_mode == 'object': + val = PackedDBobject(attr.db_value, "objectdb") + elif db_mode == 'player': + val = PackedDBobject(attr.db_value, "playerdb") + elif db_mode == 'script': + val = PackedDBobject(attr.db_value, "scriptdb") + elif db_mode == 'help': + val = PackedDBobject(attr.db_value, "helpentry") + else: + val = PackedDBobject(attr.db_value, db_mode) # channel, msg + attr.value = val + + db.delete_column('scripts_scriptattribute', 'db_mode') + + + def backwards(self, orm): + + # Adding field 'ScriptAttribute.db_mode' + db.add_column('scripts_scriptattribute', 'db_mode', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'objects.objectdb': { + 'Meta': {'object_name': 'ObjectDB'}, + 'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'players.playerdb': { + 'Meta': {'object_name': 'PlayerDB'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) + }, + 'scripts.scriptattribute': { + 'Meta': {'object_name': 'ScriptAttribute'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['scripts.ScriptDB']"}), + 'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'scripts.scriptdb': { + 'Meta': {'object_name': 'ScriptDB'}, + 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'db_interval': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'db_is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True', 'blank': 'True'}), + 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'db_persistent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_repeats': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'db_start_delay': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['scripts'] diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index ce8105f64b..215ab87647 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -30,6 +30,7 @@ import traceback from django.db import models from django.conf import settings from django.utils.encoding import smart_str +from django.contrib.contenttypes.models import ContentType from src.utils.idmapper.models import SharedMemoryModel from src.typeclasses import managers from src.locks.lockhandler import LockHandler @@ -42,12 +43,12 @@ PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY] # Note that these have to be updated if directory structure changes. PARENTS = { "typeclass":"src.typeclasses.typeclass.TypeClass", - "object":"src.objects.models.ObjectDB", - "player":"src.players.models.PlayerDB", - "script":"src.scripts.models.ScriptDB", + "objectdb":"src.objects.models.ObjectDB", + "layerdb":"src.players.models.PlayerDB", + "scriptdb":"src.scripts.models.ScriptDB", "msg":"src.comms.models.Msg", "channel":"src.comms.models.Channel", - "help":"src.help.models.HelpEntry"} + "helpentry":"src.help.models.HelpEntry"} # cached typeclasses for all typed models TYPECLASS_CACHE = {} @@ -63,6 +64,12 @@ def reset(): # #------------------------------------------------------------ +class PackedDBobject(object): + "Simple helper class for storing database object ids." + def __init__(self, ID, db_model): + self.id = ID + self.db_model = db_model + class Attribute(SharedMemoryModel): """ Abstract django model. @@ -94,8 +101,6 @@ class Attribute(SharedMemoryModel): db_key = models.CharField(max_length=255) # access through the value property db_value = models.TextField(blank=True, null=True) - # tells us what type of data is stored in the attribute - db_mode = models.CharField(max_length=20, null=True, blank=True) # Lock storage db_lock_storage = models.TextField(blank=True) # references the object the attribute is linked to (this is set @@ -106,7 +111,7 @@ class Attribute(SharedMemoryModel): # Database manager objects = managers.AttributeManager() - + # Lock handler self.locks def __init__(self, *args, **kwargs): "Initializes the parent first -important!" @@ -143,23 +148,6 @@ class Attribute(SharedMemoryModel): raise Exception("Cannot delete attribute key!") key = property(key_get, key_set, key_del) - # mode property (wraps db_mode) - #@property - def mode_get(self): - "Getter. Allows for value = self.mode" - return self.db_mode - #@mode.setter - def mode_set(self, value): - "Setter. Allows for self.mode = value" - self.db_mode = value - self.save() - #@mode.deleter - def mode_del(self): - "Deleter. Allows for del self.mode" - self.db_mode = None - self.save() - mode = property(mode_get, mode_set, mode_del) - # obj property (wraps db_obj) #@property def obj_get(self): @@ -198,44 +186,14 @@ class Attribute(SharedMemoryModel): """ Getter. Allows for value = self.value. """ - db_value = self.db_value - db_mode = self.db_mode try: - if not db_mode: - # it's a string, just return plain db_value below - pass - elif db_mode == 'pickle': - db_value = pickle.loads(str(db_value)) - elif db_mode == 'object': - from src.objects.models import ObjectDB - db_value = ObjectDB.objects.dbref_search(db_value) - elif db_mode == 'script': - from src.scripts.models import ScriptDB - db_value = ScriptDB.objects.dbref_search(db_value) - elif db_mode == 'player': - from src.players.models import PlayerDB - db_value = PlayerDB.objects.get(id=int(db_value)) - elif db_mode == 'msg': - from src.comms.models import Msg - db_value = Msg.objects.objects.get(id=int(db_value)) - elif db_mode == 'channel': - from src.comms.models import Channel - db_value = Channel.objects.get(id=int(db_value)) - elif db_mode == 'help': - from src.help.models import HelpEntry - db_value = HelpEntry.objects.get(id=int(db_value)) - except Exception: - logger.log_trace() #TODO: Remove when stable? - db_value = None - return db_value + return self.validate_data(pickle.loads(str(self.db_value))) + except pickle.UnpicklingError: + return self.db_value #@value.setter def value_set(self, new_value): "Setter. Allows for self.value = value" - new_value, mode = self._convert_value(new_value) - if mode == "pickle": - new_value = pickle.dumps(new_value) #,pickle.HIGHEST_PROTOCOL) - self.db_value = new_value - self.db_mode = mode + self.db_value = pickle.dumps(self.validate_data(new_value)) self.save() #@value.deleter def value_del(self): @@ -272,52 +230,55 @@ class Attribute(SharedMemoryModel): def __unicode__(self): return u"%s(%s)" % (self.key, self.id) - def _convert_value(self, in_value): + def validate_data(self, item): """ - We have to be careful as to what we store. Some things, such - as django model instances, cannot be directly stored/pickled - in an attribute, so we have to be clever about it. Types of - objects and how they are handled: - * str - stored directly in field - * django model object - store its dbref in field - * any other python structure - pickle in field + We have to make sure to not store database objects raw, since this will + crash the system. Instead we must store their IDs and make sure to convert + back when the attribute is read back later. - """ - - if isinstance(in_value, basestring): - # (basestring matches both str and unicode) - # strings we just store directly. - return in_value, None + We handle only lists and dicts for iterables. + """ + if isinstance(item, basestring): + # a string is unmodified + ret = item + elif type(item) == PackedDBobject: + # unpack a previously packed object + try: + mclass = ContentType.objects.get(model=item.db_model).model_class() + try: + ret = mclass.objects.dbref_search(item.id) + except AttributeError: + ret = mclass.objects.get(id=item.id) + except Exception: + logger.log_trace("Attribute error: %s, %s" % (item.db_model, item.id)) #TODO: Remove when stable? + ret = None + elif type(item) == dict: + # handle dictionaries + ret = {} + for key, it in item.items(): + ret[key] = self.validate_data(it) + elif is_iter(item): + # Note: ALL other iterables are considered to be lists! + ret = [] + for it in item: + ret.append(self.validate_data(it)) + elif has_parent('django.db.models.base.Model', item) or has_parent(PARENTS['typeclass'], item): + # db models must be stored as dbrefs + db_model = [parent for parent, path in PARENTS.items() if has_parent(path, item)] + if db_model and db_model[0] == 'typeclass': + # the typeclass alone can't help us, we have to know the db object. + db_model = [parent for parent, path in PARENTS.items() + if has_parent(path, item.dbobj)] + if db_model: + # store the object in an easily identifiable container + ret = PackedDBobject(str(item.id), db_model[0]) + else: + # not a valid object - some third-party class or primitive? + ret = item + else: + ret = item - if is_iter(in_value): - # an iterable. This is normally something to pickle, - # but we have to be careful so as to not find - # django model instances nested in the iterable. - pass #TODO! - - - if not has_parent('django.db.models.base.Model', in_value) \ - and not has_parent(PARENTS['typeclass'], in_value): - # non-django models that are not strings we pickle - #print "type identified: to_pickle" - #print "found a non-django parent." - return in_value, 'pickle' - - # this is a db model. Try to determine what type of db object it is. - db_type = [parent for parent, path in PARENTS.items() - if has_parent(path, in_value)] - if db_type and db_type[0] == 'typeclass': - # the typeclass alone can't help us, we have to know the db object. - db_type = [parent for parent, path in PARENTS.items() - if has_parent(path, in_value.dbobj)] - - if not db_type: - # no match; maybe it's a non-model from inside django(?). - return in_value, "pickle" - - # it's a db model. Return its dbref as a string instead. - #print "type identified: %s" % db_type[0] - return str(in_value.id), db_type[0] + return ret def access(self, accessing_obj, access_type='read', default=False): """ @@ -329,7 +290,6 @@ class Attribute(SharedMemoryModel): return self.locks.check(accessing_obj, access_type=access_type, default=default) - #------------------------------------------------------------ # # Typed Objects