summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--devel/management/commands/pgp_import.py4
-rw-r--r--devel/management/commands/rematch_developers.py4
-rw-r--r--devel/management/commands/reporead.py13
-rw-r--r--devel/management/commands/reporead_inotify.py2
-rw-r--r--devel/views.py6
-rw-r--r--main/migrations/0065_auto__add_field_package_signature_bytes.py114
-rw-r--r--main/migrations/0066_move_signature_data.py125
-rw-r--r--main/migrations/0067_auto__del_field_package_pgp_signature.py113
-rw-r--r--main/models.py15
-rw-r--r--mirrors/admin.py6
-rw-r--r--mirrors/management/commands/mirrorcheck.py2
-rw-r--r--packages/admin.py4
-rw-r--r--packages/views/flag.py2
-rw-r--r--packages/views/signoff.py2
-rw-r--r--requirements.txt4
-rw-r--r--requirements_prod.txt4
-rw-r--r--templates/devel/clock.html1
-rw-r--r--templates/devel/index.html1
-rw-r--r--templates/devel/packages.html1
-rw-r--r--templates/devel/stats.html1
-rw-r--r--templates/mirrors/error_table.html1
-rw-r--r--templates/mirrors/mirror_details.html1
-rw-r--r--templates/mirrors/mirrors.html1
-rw-r--r--templates/mirrors/status_table.html1
-rw-r--r--templates/news/list.html2
-rw-r--r--templates/packages/differences.html1
-rw-r--r--templates/packages/groups.html1
-rw-r--r--templates/packages/packages_list.html1
-rw-r--r--templates/packages/search.html1
-rw-r--r--templates/packages/signoffs.html1
-rw-r--r--templates/packages/stale_relations.html1
-rw-r--r--templates/releng/release_list.html1
-rw-r--r--templates/todolists/list.html1
-rw-r--r--templates/todolists/view.html1
-rw-r--r--todolists/views.py2
35 files changed, 407 insertions, 34 deletions
diff --git a/devel/management/commands/pgp_import.py b/devel/management/commands/pgp_import.py
index faa9ff5..3f557fe 100644
--- a/devel/management/commands/pgp_import.py
+++ b/devel/management/commands/pgp_import.py
@@ -134,7 +134,7 @@ def import_keys(keyring):
logger.info("creating or finding %d keys", len(keydata))
created_ct = updated_ct = 0
- with transaction.commit_on_success():
+ with transaction.atomic():
finder = UserFinder()
# we are dependent on parents coming before children; parse_keydata
# uses an OrderedDict to ensure this is the case.
@@ -232,7 +232,7 @@ def import_signatures(keyring):
logger.info("creating or finding up to %d signatures", len(pruned_edges))
created_ct = updated_ct = 0
- with transaction.commit_on_success():
+ with transaction.atomic():
for edge in pruned_edges:
sig, created = PGPSignature.objects.get_or_create(
signer=edge.signer, signee=edge.signee,
diff --git a/devel/management/commands/rematch_developers.py b/devel/management/commands/rematch_developers.py
index 2b37958..7a06e08 100644
--- a/devel/management/commands/rematch_developers.py
+++ b/devel/management/commands/rematch_developers.py
@@ -44,7 +44,7 @@ class Command(NoArgsCommand):
match_packager(finder)
match_flagrequest(finder)
-@transaction.commit_on_success
+@transaction.atomic
def match_packager(finder):
logger.info("getting all unmatched packager strings")
package_count = matched_count = 0
@@ -70,7 +70,7 @@ def match_packager(finder):
package_count, matched_count)
-@transaction.commit_on_success
+@transaction.atomic
def match_flagrequest(finder):
logger.info("getting all flag request email addresses from unknown users")
req_count = matched_count = 0
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index ff7a842..2b565cf 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -13,6 +13,7 @@ Example:
./manage.py reporead i686 /tmp/core.db.tar.gz
"""
+from base64 import b64decode
from collections import defaultdict
from copy import copy
import io
@@ -217,7 +218,7 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None):
dbpkg.packager_str = repopkg.packager
# attempt to find the corresponding django user for this string
dbpkg.packager = finder.find(repopkg.packager)
- dbpkg.pgp_signature = repopkg.pgpsig
+ dbpkg.signature_bytes = b64decode(repopkg.pgpsig.encode('utf-8'))
if timestamp:
dbpkg.last_update = timestamp
@@ -302,7 +303,7 @@ def update_common(archname, reponame, pkgs, sanity_check=True):
# If isolation level is repeatable-read, we need to ensure each package
# update starts a new transaction and re-queries the database as
# necessary to guard against simultaneous updates.
- with transaction.commit_on_success():
+ with transaction.atomic():
# force the transaction dirty, even though we will only do reads
transaction.set_dirty()
@@ -365,7 +366,7 @@ def db_update(archname, reponame, pkgs, force=False):
dbpkg = Package(pkgname=pkg.name, arch=architecture, repo=repository,
created=timestamp)
try:
- with transaction.commit_on_success():
+ with transaction.atomic():
populate_pkg(dbpkg, pkg, timestamp=timestamp)
Update.objects.log_update(None, dbpkg)
except IntegrityError:
@@ -380,7 +381,7 @@ def db_update(archname, reponame, pkgs, force=False):
for pkgname in (dbset - syncset):
logger.info("Removing package %s", pkgname)
dbpkg = dbdict[pkgname]
- with transaction.commit_on_success():
+ with transaction.atomic():
Update.objects.log_update(dbpkg, None)
# no race condition here as long as simultaneous threads both
# issue deletes; second delete will be a no-op
@@ -403,7 +404,7 @@ def db_update(archname, reponame, pkgs, force=False):
# The odd select_for_update song and dance here are to ensure
# simultaneous updates don't happen on a package, causing
# files/depends/all related items to be double-imported.
- with transaction.commit_on_success():
+ with transaction.atomic():
dbpkg = Package.objects.select_for_update().get(id=dbpkg.id)
if not force and pkg_same_version(pkg, dbpkg):
logger.debug("Package %s was already updated", pkg.name)
@@ -431,7 +432,7 @@ def filesonly_update(archname, reponame, pkgs, force=False):
# The odd select_for_update song and dance here are to ensure
# simultaneous updates don't happen on a package, causing
# files to be double-imported.
- with transaction.commit_on_success():
+ with transaction.atomic():
if not dbpkg.files_last_update or not dbpkg.last_update:
pass
elif not force and dbpkg.files_last_update >= dbpkg.last_update:
diff --git a/devel/management/commands/reporead_inotify.py b/devel/management/commands/reporead_inotify.py
index 6aa4e0e..e3c720b 100644
--- a/devel/management/commands/reporead_inotify.py
+++ b/devel/management/commands/reporead_inotify.py
@@ -66,7 +66,7 @@ class Command(BaseCommand):
if hasattr(thread, 'cancel'):
thread.cancel()
- @transaction.commit_on_success
+ @transaction.atomic
def setup_notifier(self):
'''Set up and configure the inotify machinery and logic.
This takes the provided or default path_template and builds a list of
diff --git a/devel/views.py b/devel/views.py
index b6e8582..29954b5 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -167,7 +167,7 @@ def change_profile(request):
request.user.email = form.cleaned_data['email']
if form.cleaned_data['passwd1']:
request.user.set_password(form.cleaned_data['passwd1'])
- with transaction.commit_on_success():
+ with transaction.atomic():
request.user.save()
profile_form.save()
return HttpResponseRedirect('/devel/')
@@ -275,7 +275,7 @@ def report(request, report_name, username=None):
cutoff = timedelta(hours=24)
filtered = []
packages = packages.select_related(
- 'arch', 'repo', 'packager').filter(pgp_signature__isnull=False)
+ 'arch', 'repo', 'packager').filter(signature_bytes__isnull=False)
known_keys = DeveloperKey.objects.select_related(
'owner').filter(owner__isnull=False)
known_keys = {dk.key: dk for dk in known_keys}
@@ -335,7 +335,7 @@ def new_user_form(request):
if request.POST:
form = NewUserForm(request.POST)
if form.is_valid():
- with transaction.commit_on_success():
+ with transaction.atomic():
form.save()
log_addition(request, form.instance.user)
return HttpResponseRedirect('/admin/auth/user/%d/' % \
diff --git a/main/migrations/0065_auto__add_field_package_signature_bytes.py b/main/migrations/0065_auto__add_field_package_signature_bytes.py
new file mode 100644
index 0000000..a954166
--- /dev/null
+++ b/main/migrations/0065_auto__add_field_package_signature_bytes.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.add_column('packages', 'signature_bytes',
+ self.gf('django.db.models.fields.BinaryField')(null=True),
+ keep_default=True)
+
+ def backwards(self, orm):
+ db.delete_column('packages', 'signature_bytes')
+
+
+ models = {
+ 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'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+ u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 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'main.arch': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Arch', 'db_table': "'arches'"},
+ 'agnostic': ('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': '255'}),
+ 'required_signoffs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'})
+ },
+ u'main.donor': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ u'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Repo']"}),
+ 'signature_bytes': ('django.db.models.fields.BinaryField', [], {'null': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ u'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Package']"})
+ },
+ u'main.repo': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'staging': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ }
+ }
+
+ complete_apps = ['main']
diff --git a/main/migrations/0066_move_signature_data.py b/main/migrations/0066_move_signature_data.py
new file mode 100644
index 0000000..75fcfa9
--- /dev/null
+++ b/main/migrations/0066_move_signature_data.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+from base64 import b64decode, b64encode
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ pkgs = orm.Package.objects.only(
+ 'id', 'pgp_signature', 'signature_bytes').all()
+ for pkg in pkgs:
+ if not pkg.pgp_signature:
+ continue
+ pkg.signature_bytes = b64decode(pkg.pgp_signature.encode('utf-8'))
+ pkg.save()
+
+ def backwards(self, orm):
+ pkgs = orm.Package.objects.only(
+ 'id', 'pgp_signature', 'signature_bytes').all()
+ for pkg in pkgs:
+ if not pkg.signature_bytes:
+ continue
+ pkg.pgp_signature = b64encode(pkg.signature_bytes).decode('utf-8')
+ pkg.save()
+
+
+ models = {
+ 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'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+ u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 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'main.arch': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Arch', 'db_table': "'arches'"},
+ 'agnostic': ('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': '255'}),
+ 'required_signoffs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'})
+ },
+ u'main.donor': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ u'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Repo']"}),
+ 'signature_bytes': ('django.db.models.fields.BinaryField', [], {'null': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ u'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Package']"})
+ },
+ u'main.repo': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'staging': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ }
+ }
+
+ complete_apps = ['main']
+ symmetrical = True
diff --git a/main/migrations/0067_auto__del_field_package_pgp_signature.py b/main/migrations/0067_auto__del_field_package_pgp_signature.py
new file mode 100644
index 0000000..8c1d9b7
--- /dev/null
+++ b/main/migrations/0067_auto__del_field_package_pgp_signature.py
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.delete_column('packages', 'pgp_signature')
+
+ def backwards(self, orm):
+ db.add_column('packages', 'pgp_signature',
+ self.gf('django.db.models.fields.TextField')(null=True, blank=True),
+ keep_default=True)
+
+
+ models = {
+ 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'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+ u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 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'main.arch': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Arch', 'db_table': "'arches'"},
+ 'agnostic': ('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': '255'}),
+ 'required_signoffs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'})
+ },
+ u'main.donor': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ u'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Repo']"}),
+ 'signature_bytes': ('django.db.models.fields.BinaryField', [], {'null': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ u'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Package']"})
+ },
+ u'main.repo': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'staging': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ }
+ }
+
+ complete_apps = ['main']
diff --git a/main/models.py b/main/models.py
index 2ace010..3f96408 100644
--- a/main/models.py
+++ b/main/models.py
@@ -1,4 +1,3 @@
-from base64 import b64decode
from datetime import datetime
from itertools import groupby
from pgpdump import BinaryData
@@ -97,7 +96,7 @@ class Package(models.Model):
pkgver = models.CharField(max_length=255)
pkgrel = models.CharField(max_length=255)
epoch = models.PositiveIntegerField(default=0)
- pkgdesc = models.TextField(null=True)
+ pkgdesc = models.TextField('description', null=True)
url = models.CharField(max_length=255, null=True)
filename = models.CharField(max_length=255)
compressed_size = PositiveBigIntegerField()
@@ -106,10 +105,10 @@ class Package(models.Model):
last_update = models.DateTimeField(db_index=True)
files_last_update = models.DateTimeField(null=True, blank=True)
created = models.DateTimeField()
- packager_str = models.CharField(max_length=255)
+ packager_str = models.CharField('packager string', max_length=255)
packager = models.ForeignKey(User, null=True, blank=True,
on_delete=models.SET_NULL)
- pgp_signature = models.TextField(null=True, blank=True)
+ signature_bytes = models.BinaryField('PGP signature', null=True)
flag_date = models.DateTimeField(null=True, blank=True)
objects = PackageManager()
@@ -140,13 +139,9 @@ class Package(models.Model):
@property
def signature(self):
- try:
- data = b64decode(self.pgp_signature.encode('utf-8'))
- except TypeError:
- return None
- if not data:
+ if not self.signature_bytes:
return None
- data = BinaryData(data)
+ data = BinaryData(self.signature_bytes)
packets = list(data.packets())
return packets[0]
diff --git a/mirrors/admin.py b/mirrors/admin.py
index d0f2f47..e35d9ce 100644
--- a/mirrors/admin.py
+++ b/mirrors/admin.py
@@ -10,6 +10,7 @@ from .models import (Mirror, MirrorProtocol, MirrorUrl, MirrorRsync,
class MirrorUrlForm(forms.ModelForm):
class Meta:
model = MirrorUrl
+ fields = ('url', 'country', 'active')
def clean_url(self):
# is this a valid-looking URL?
@@ -39,6 +40,7 @@ class MirrorUrlInlineAdmin(admin.TabularInline):
class MirrorRsyncForm(forms.ModelForm):
class Meta:
model = MirrorRsync
+ fields = ('ip',)
class MirrorRsyncInlineAdmin(admin.TabularInline):
@@ -50,6 +52,10 @@ class MirrorRsyncInlineAdmin(admin.TabularInline):
class MirrorAdminForm(forms.ModelForm):
class Meta:
model = Mirror
+ fields = ('name', 'tier', 'upstream', 'admin_email', 'alternate_email',
+ 'public', 'active', 'isos', 'rsync_user', 'rsync_password',
+ 'notes')
+
upstream = forms.ModelChoiceField(
queryset=Mirror.objects.filter(tier__gte=0, tier__lte=1),
required=False)
diff --git a/mirrors/management/commands/mirrorcheck.py b/mirrors/management/commands/mirrorcheck.py
index 3f026c3..e48aa42 100644
--- a/mirrors/management/commands/mirrorcheck.py
+++ b/mirrors/management/commands/mirrorcheck.py
@@ -241,7 +241,7 @@ class MirrorCheckPool(object):
thread.daemon = True
self.threads.append(thread)
- @transaction.commit_on_success
+ @transaction.atomic
def run(self):
logger.debug("starting threads")
for thread in self.threads:
diff --git a/packages/admin.py b/packages/admin.py
index 820bbb2..4680c75 100644
--- a/packages/admin.py
+++ b/packages/admin.py
@@ -20,7 +20,7 @@ class FlagRequestAdmin(admin.ModelAdmin):
ordering = ('-created',)
date_hierarchy = 'created'
- def queryset(self, request):
+ def get_queryset(self, request):
qs = super(FlagRequestAdmin, self).queryset(request)
return qs.select_related('repo', 'user')
@@ -42,7 +42,7 @@ class SignoffSpecificationAdmin(admin.ModelAdmin):
ordering = ('-created',)
date_hierarchy = 'created'
- def queryset(self, request):
+ def get_queryset(self, request):
qs = super(SignoffSpecificationAdmin, self).queryset(request)
return qs.select_related('arch', 'repo', 'user')
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 39cdcef..9fe60e2 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -84,7 +84,7 @@ def flag(request, name, repo, arch):
else:
email = form.cleaned_data['email']
- @transaction.commit_on_success
+ @transaction.atomic
def perform_updates():
current_time = now()
pkgs.update(flag_date=current_time)
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index c37aa0f..fcc6de4 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -81,7 +81,7 @@ class SignoffOptionsForm(forms.ModelForm):
def _signoff_options_all(request, name, repo):
seen_ids = set()
- with transaction.commit_on_success():
+ with transaction.atomic():
# find or create a specification for all architectures, then
# graft the form data onto them
packages = Package.objects.filter(pkgbase=name,
diff --git a/requirements.txt b/requirements.txt
index 479c213..08d8910 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
-e git+git://github.com/fredj/cssmin.git@master#egg=cssmin
-Django==1.5.5
+Django==1.6
IPy==0.81
Markdown==2.3.1
South==0.8.2
@@ -7,4 +7,4 @@ bencode==1.0
django-countries==1.5
jsmin==2.0.6
pgpdump==1.4
-pytz>=2013.7
+pytz>=2013.8
diff --git a/requirements_prod.txt b/requirements_prod.txt
index 2c38903..565c3c6 100644
--- a/requirements_prod.txt
+++ b/requirements_prod.txt
@@ -1,5 +1,5 @@
-e git+git://github.com/fredj/cssmin.git@master#egg=cssmin
-Django==1.5.5
+Django==1.6
IPy==0.81
Markdown==2.3.1
South==0.8.2
@@ -10,4 +10,4 @@ pgpdump==1.4
psycopg2==2.5.1
pyinotify==0.9.4
python-memcached==1.53
-pytz>=2013.7
+pytz>=2013.8
diff --git a/templates/devel/clock.html b/templates/devel/clock.html
index 2c5bfac..9d672f1 100644
--- a/templates/devel/clock.html
+++ b/templates/devel/clock.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load flags %}
{% load tz %}
diff --git a/templates/devel/index.html b/templates/devel/index.html
index 03e5d73..c0c437a 100644
--- a/templates/devel/index.html
+++ b/templates/devel/index.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load cache %}
{% load package_extras %}
diff --git a/templates/devel/packages.html b/templates/devel/packages.html
index d6aab49..74aebf2 100644
--- a/templates/devel/packages.html
+++ b/templates/devel/packages.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load attributes %}
{% load package_extras %}
diff --git a/templates/devel/stats.html b/templates/devel/stats.html
index 2dbe475..9b3b1d2 100644
--- a/templates/devel/stats.html
+++ b/templates/devel/stats.html
@@ -1,3 +1,4 @@
+{% load cycle from future %}
{% load cache %}
{% cache 60 dev-dash-by-arch %}
diff --git a/templates/mirrors/error_table.html b/templates/mirrors/error_table.html
index 6054814..cd7265a 100644
--- a/templates/mirrors/error_table.html
+++ b/templates/mirrors/error_table.html
@@ -1,3 +1,4 @@
+{% load cycle from future %}
{% load flags mirror_status %}
<table id="errorlog_mirrors" class="results">
<thead>
diff --git a/templates/mirrors/mirror_details.html b/templates/mirrors/mirror_details.html
index 1c7f563..f2ffce2 100644
--- a/templates/mirrors/mirror_details.html
+++ b/templates/mirrors/mirror_details.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load mirror_status %}
{% load flags %}
diff --git a/templates/mirrors/mirrors.html b/templates/mirrors/mirrors.html
index 458b693..4276b30 100644
--- a/templates/mirrors/mirrors.html
+++ b/templates/mirrors/mirrors.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% block title %}Arch Linux - Mirror Overview{% endblock %}
diff --git a/templates/mirrors/status_table.html b/templates/mirrors/status_table.html
index 2817542..6fc07a3 100644
--- a/templates/mirrors/status_table.html
+++ b/templates/mirrors/status_table.html
@@ -1,3 +1,4 @@
+{% load cycle from future %}
{% load flags mirror_status %}
<table id="{{ table_id }}" class="results">
<thead>
diff --git a/templates/news/list.html b/templates/news/list.html
index e85cece..4acbc7e 100644
--- a/templates/news/list.html
+++ b/templates/news/list.html
@@ -1,4 +1,6 @@
{% extends "base.html" %}
+{% load cycle from future %}
+
{% block title %}Arch Linux - News{% endblock %}
{% block head %}
diff --git a/templates/packages/differences.html b/templates/packages/differences.html
index b0b9b41..f137126 100644
--- a/templates/packages/differences.html
+++ b/templates/packages/differences.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% block title %}Arch Linux - Package Differences Reports{% endblock %}
diff --git a/templates/packages/groups.html b/templates/packages/groups.html
index c135791..ee4f031 100644
--- a/templates/packages/groups.html
+++ b/templates/packages/groups.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% block title %}Arch Linux - Package Groups{% if arch %} - {{ arch }}{% endif %}{% endblock %}
diff --git a/templates/packages/packages_list.html b/templates/packages/packages_list.html
index 3dcc03d..0a1627f 100644
--- a/templates/packages/packages_list.html
+++ b/templates/packages/packages_list.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load package_extras %}
diff --git a/templates/packages/search.html b/templates/packages/search.html
index f50bc8b..d312c37 100644
--- a/templates/packages/search.html
+++ b/templates/packages/search.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load package_extras %}
{% block title %}Arch Linux - Package Database{% endblock %}
diff --git a/templates/packages/signoffs.html b/templates/packages/signoffs.html
index 807b613..83f81d3 100644
--- a/templates/packages/signoffs.html
+++ b/templates/packages/signoffs.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load package_extras %}
diff --git a/templates/packages/stale_relations.html b/templates/packages/stale_relations.html
index 14fac39..76f3205 100644
--- a/templates/packages/stale_relations.html
+++ b/templates/packages/stale_relations.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% block title %}Arch Linux - Stale Package Relations{% endblock %}
diff --git a/templates/releng/release_list.html b/templates/releng/release_list.html
index 7197cc8..f7e9037 100644
--- a/templates/releng/release_list.html
+++ b/templates/releng/release_list.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% block title %}Arch Linux - Releases{% endblock %}
diff --git a/templates/todolists/list.html b/templates/todolists/list.html
index 56f9874..7f0368d 100644
--- a/templates/todolists/list.html
+++ b/templates/todolists/list.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% block title %}Arch Linux - Todo Lists{% endblock %}
diff --git a/templates/todolists/view.html b/templates/todolists/view.html
index b26cd91..a3ee547 100644
--- a/templates/todolists/view.html
+++ b/templates/todolists/view.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load cycle from future %}
{% load static from staticfiles %}
{% load package_extras %}
{% load todolists %}
diff --git a/todolists/views.py b/todolists/views.py
index ff75686..c37c13f 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -147,7 +147,7 @@ class DeleteTodolist(DeleteView):
success_url = '/todo/'
-@transaction.commit_on_success
+@transaction.atomic
def create_todolist_packages(form, creator=None):
package_names = form.package_names()
packages = form.packages()