diff --git a/modoboa/extensions/admin/forms/domain.py b/modoboa/extensions/admin/forms/domain.py index 9bdfe81c5..99d955422 100644 --- a/modoboa/extensions/admin/forms/domain.py +++ b/modoboa/extensions/admin/forms/domain.py @@ -102,7 +102,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): return cleaned_data def update_mailbox_quotas(self, domain): - """Update all quota records associated to this domain + """Update all quota records associated to this domain. This method must be called only when a domain gets renamed. As the primary key used for a quota is an email address, rename a @@ -116,9 +116,14 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): perfomant at all as it will generate one query per quota record to update. """ - for q in Quota.objects.filter(username__contains="@%s" % self.oldname).values('username'): - username = q['username'].replace('@%s' % self.oldname, '@%s' % domain.name) - Quota.objects.filter(username=q['username']).update(username=username) + for q in Quota.objects.filter(username__contains="@%s" % self.oldname): + username = q.username.replace( + '@%s' % self.oldname, '@%s' % domain.name) + newq = Quota.objects.create( + username=username, bytes=q.bytes, messages=q.messages) + q.mailbox.quota_value = newq + q.mailbox.save() + q.delete() def save(self, user, commit=True, domalias_post_create=False): """Custom save method diff --git a/modoboa/extensions/admin/migrations/0041_auto__chg_field_mailbox_quota_value__add_unique_mailbox_quota_value.py b/modoboa/extensions/admin/migrations/0041_auto__chg_field_mailbox_quota_value__add_unique_mailbox_quota_value.py new file mode 100644 index 000000000..9bc6a8f19 --- /dev/null +++ b/modoboa/extensions/admin/migrations/0041_auto__chg_field_mailbox_quota_value__add_unique_mailbox_quota_value.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Mailbox.quota_value' + db.alter_column(u'admin_mailbox', 'quota_value_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['admin.Quota'], unique=True)) + # Adding unique constraint on 'Mailbox', fields ['quota_value'] + db.create_unique(u'admin_mailbox', ['quota_value_id']) + + + def backwards(self, orm): + # Removing unique constraint on 'Mailbox', fields ['quota_value'] + db.delete_unique(u'admin_mailbox', ['quota_value_id']) + + + # Changing field 'Mailbox.quota_value' + db.alter_column(u'admin_mailbox', 'quota_value_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['admin.Quota'])) + + models = { + 'admin.alias': { + 'Meta': {'ordering': "['domain__name', 'address']", 'unique_together': "(('address', 'domain'),)", 'object_name': 'Alias'}, + 'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'aliases': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Alias']", 'symmetrical': 'False'}), + 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'extmboxes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Mailbox']", 'symmetrical': 'False'}) + }, + 'admin.domain': { + 'Meta': {'ordering': "['name']", 'object_name': 'Domain'}, + 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'quota': ('django.db.models.fields.IntegerField', [], {}) + }, + 'admin.domainalias': { + 'Meta': {'object_name': 'DomainAlias'}, + 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}) + }, + 'admin.mailbox': { + 'Meta': {'object_name': 'Mailbox'}, + 'address': ('django.db.models.fields.CharField', [], {'max_length': '252'}), + 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'quota': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'quota_value': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['admin.Quota']", 'unique': 'True'}), + 'use_domain_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"}) + }, + 'admin.mailboxoperation': { + 'Meta': {'object_name': 'MailboxOperation'}, + 'argument': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Mailbox']", 'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + }, + 'admin.objectdates': { + 'Meta': {'object_name': 'ObjectDates'}, + 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) + }, + 'admin.quota': { + 'Meta': {'object_name': 'Quota'}, + 'bytes': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'messages': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'username': ('django.db.models.fields.EmailField', [], {'max_length': '254', 'primary_key': 'True'}) + }, + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'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'}), + u'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'}) + }, + u'core.objectaccess': { + 'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"}) + }, + u'core.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '254', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_local': ('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': '256'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '254'}) + } + } + + complete_apps = ['admin'] \ No newline at end of file diff --git a/modoboa/extensions/admin/models/mailbox.py b/modoboa/extensions/admin/models/mailbox.py index 233fde8e1..6b137358c 100644 --- a/modoboa/extensions/admin/models/mailbox.py +++ b/modoboa/extensions/admin/models/mailbox.py @@ -64,7 +64,7 @@ class Mailbox(AdminObject): ) quota = models.PositiveIntegerField() use_domain_quota = models.BooleanField(default=False) - quota_value = models.ForeignKey(Quota, to_field="username") + quota_value = models.OneToOneField(Quota, to_field="username") domain = models.ForeignKey(Domain) user = models.ForeignKey(User) @@ -146,16 +146,25 @@ class Mailbox(AdminObject): ) def rename(self, address, domain): - """Rename the mailbox + """Rename the mailbox. + + To update the associated Quota record, we must create a new + one first, update the foreign key and then we can delete the + original record! :param string address: the new mailbox's address (local part) :param Domain domain: the new mailbox's domain + """ old_mail_home = self.mail_home - qs = Quota.objects.filter(username=self.full_address) self.address = address self.domain = domain - qs.update(username=self.full_address) + old_qvalue = self.quota_value + self.quota_value = Quota.objects.create( + username=self.full_address, bytes=old_qvalue.bytes, + messages=old_qvalue.messages + ) + old_qvalue.delete() self.rename_dir(old_mail_home) def delete_dir(self): diff --git a/modoboa/extensions/admin/tests/account.py b/modoboa/extensions/admin/tests/account.py index 5ad4d556f..178f8c814 100644 --- a/modoboa/extensions/admin/tests/account.py +++ b/modoboa/extensions/admin/tests/account.py @@ -46,6 +46,7 @@ class AccountTestCase(ModoTestCase): ) mb = Mailbox.objects.get(pk=mb.id) self.assertEqual(mb.full_address, "pouet@test.com") + self.assertEqual(mb.quota_value.username, "pouet@test.com") self.ajax_post( reverse("modoboa.extensions.admin.views.identity.delaccount", args=[account.id]),