72

I am using custom permissions in my Django models like this:

class T21Turma(models.Model):
    class Meta:
        permissions = (("can_view_boletim", "Can view boletim"),
                       ("can_view_mensalidades", "Can view mensalidades"),)

The problem is that when I add a permission to the list it doesn't get added to the auth_permission table when I run syncdb. What am I doing wrong. If it makes any difference I am using south for database migrations.

Vebjorn Ljosa
  • 17,438
  • 13
  • 70
  • 88
gerdemb
  • 11,275
  • 17
  • 65
  • 73

5 Answers5

60

South does not track django.contrib.auth permissions. See ticket #211 for more information.

One of the comments on the ticket suggests that using the --all option on syncdb may solve the problem.

Daniel Naab
  • 22,690
  • 8
  • 54
  • 55
  • 1
    Sounds like exactly my problem except that I can't get the --all switch to work with syncdb nor do I find the switch documented anywhere. – gerdemb Nov 16 '09 at 20:07
  • 5
    This worked after I upgraded to south 0.6.2 (I was running 0.5) previously. – gerdemb Nov 18 '09 at 20:23
  • This just gives me the error "DatabaseError: value too long for type character varying(50)" – Cerin Aug 15 '13 at 16:28
  • that means you have a value already in the DB that is longer than 50 chars. – caesarsol Oct 23 '13 at 14:48
  • @Cerin - I got that when my permission description was over 50 characters. Seems a likely place to make that mistake. – Rob Grant Aug 18 '14 at 11:08
48

If you want "manage.py migrate" to do everything (without calling syncdb --all). You need to create new permissions with a migration:

user@host> manage.py datamigration myapp add_perm_foo --freeze=contenttypes --freeze=auth

Edit the created file:

class Migration(DataMigration):

    def forwards(self, orm):
        "Write your forwards methods here."
        ct, created = orm['contenttypes.ContentType'].objects.get_or_create(
            model='mymodel', app_label='myapp') # model must be lowercase!
        perm, created = orm['auth.permission'].objects.get_or_create(
            content_type=ct, codename='mymodel_foo', defaults=dict(name=u'Verbose Name'))
Bouke
  • 11,768
  • 7
  • 68
  • 102
guettli
  • 25,042
  • 81
  • 346
  • 663
  • 2
    there is no need for last three lines, `defaults` keyword argument of `get_or_create` can be used instead [link](https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.get_or_create) – Ivan Virabyan Aug 02 '11 at 10:15
  • 5
    If you're using this method you need to add the options `--freeze=contenttypes --freeze=auth` to the datamigration command. Otherwise you'll get the error @balmaster mentions below. E.g: `manage.py datamigration myapp add_perm_foo --freeze=contenttypes --freeze=auth` – calebbrown Dec 20 '11 at 07:03
27

This worked for me:

./manage.py update_permissions

It is a django-extensions thing.

wouldnt
  • 622
  • 6
  • 8
  • 1
    "DatabaseError: value too long for type character varying(50)". The cause is my permission's name was too long, but that's a horribly unhelpful error message, especially when I'm adding dozens of new permissions. – Cerin Aug 15 '13 at 16:44
20

You can connect to the post_migrate signal in order to update the permissions after migration. I use the following code, slightly modified from Dev with Passion and originally from django-extensions.

# Add to your project-level __init__.py

from south.signals import post_migrate

def update_permissions_after_migration(app,**kwargs):
    """
    Update app permission just after every migration.
    This is based on app django_extensions update_permissions management command.
    """
    from django.conf import settings
    from django.db.models import get_app, get_models
    from django.contrib.auth.management import create_permissions

    create_permissions(get_app(app), get_models(), 2 if settings.DEBUG else 0)

post_migrate.connect(update_permissions_after_migration)
Vebjorn Ljosa
  • 17,438
  • 13
  • 70
  • 88
  • 1
    This has an issue when working with something like gunicorn to run the app, namely it fails at finding environmental variable that is set in the wsgi.py file. – Pier1 Sys Jul 31 '13 at 00:07
2

When i runnning migration with following code

ct, created = orm['contenttypes.ContentType'].objects.get_or_create(model='mymodel',     app_label='myapp') # model must bei lowercase!
perm, created = orm['auth.permission'].objects.get_or_create(content_type=ct, codename='mymodel_foo')

I getting folloving error

File "C:\Python26\lib\site-packages\south-0.7.3-py2.6.egg\south\orm.py", line 170, in  __getitem__
raise KeyError("The model '%s' from the app '%s' is not available in this migration." % (model, app))
KeyError: "The model 'contenttype' from the app 'contenttypes' is not available in this migration."

To prevent this error, i modified the code

from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission

class Migration(DataMigration):

    def forwards(self, orm):
        "Write your forwards methods here."
        ct = ContentType.objects.get(model='mymodel', app_label='myapp') 
        perm, created = Permission.objects.get_or_create(content_type=ct, codename='mymodel_foo')
        if created:
            perm.name=u'my permission description'
            perm.save()
balmaster
  • 158
  • 1
  • 4
  • 7
    Bad idea; you should use the frozen ORM. Add `--freeze=contenttypes --freeze=auth` to your `./manage.py datamigration` command line. – Vebjorn Ljosa Jul 20 '12 at 11:00