How to add a permission to a user/group during a django migration? - django

I would like to execute the following migration:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib.auth.models import Permission
from django.db import migrations
from django.conf import settings
from django.contrib.auth.models import Group, User
def add_api_group(apps, schema_editor):
Group.objects.create(name=settings.API_USER_GROUP)
# get_or_create returns a tuple, not a Group
group = Group.objects.get(name=settings.API_USER_GROUP)
permissions = Permission.objects.filter(codename__in = [
'add_topic',
])
group.permissions.add(*permissions)
def add_api_user(apps, schema_editor):
user = User.objects.create_user(username=settings.API_USER, password=settings.API_USER_PASSWORD)
group = Group.objects.get(name=settings.API_USER_GROUP)
user.groups.add(group)
class Migration(migrations.Migration):
dependencies = [
('nd_content', '0001_initial'),
]
operations = [
migrations.RunPython(add_api_group),
migrations.RunPython(add_api_user)
]
At the last line of the migration, I issued an error to stop execution and look at the database state. The problem is the table auth_permission still has not the permissions of a model of another module, although this other module is registered as a dependecy of this migration.
I can confirm missing permissions seem to be added only after all migrations have been executed.

AttributeError: 'StateApps' object has no attribute 'label' in Django 1.10
There is a solution:
for app_config in apps.get_app_configs():
app_config.models_module = True
create_permissions(app_config, verbosity=0)
app_config.models_module = None

EDIT 2018-01-31
This answer will only work until Django 1.9. For Django 1.10 an up, please refer to the answer provided by #anton-lisenkov
Original Answer (Django<1.10)
It turns out I could do the following:
from django.contrib.auth.management import create_permissions
def add_permissions(apps, schema_editor):
apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None
Thanks #elad-silver for his answer: https://stackoverflow.com/a/34272647/854868

If you don't have to attach your permission to a personal model you can do it this way:
from django.contrib.auth.models import Permission, ContentType
def add_permission(apps, schema_editor):
content_type = ContentType.objects.get(app_label='auth', model='user') # I chose user model but you can edit it
permission = Permission(
name='Your permission description',
codename='your_codename',
content_type=content_type,
)
permission.save()

Related

ImportError: cannot import name 'Users' from partially initialized module tasks file

ImportError: cannot import name 'Users' from partially initialized module 'users.models
from .models import Users
from celery import shared_task
from django.core.mail import send_mail
from server import settings
#shared_task()
def send_mail_task(user_id):
user = Users.objects.get(id=user_id)
send_mail(
subject='Congratulations!',
message=f'Congratulations {user.username}',
from_email=settings.EMAIL_HOST_USER,
recipient_list=["waelelsafty07#gmail.com", ],
fail_silently=False,
)
print('Email sent successfully')
return f'Email sent successfully'
checking installing celery
I import my model in this way:
model = apps.get_model(app_label='users', model_name='users')
when the users is name the folder
could you post your working directory so i can see where is the files are
if you didnt customize the user model then use
from django.contrib.auth.models import User

Adding django admin permissions in a migration: Permission matching query does not exist

I wanted to add some groups and assign permissions to them in a manually written migration but if I run it on a clean DB it creates permissions only after running all migrations.
I've found this ticket: https://code.djangoproject.com/ticket/23422
but I cannot comment there (it's possible I was banned after expressing some discontent with GeoDjango docs), so I'll share an improvement over the solution there below.
In django 1.10 the following code could be used:
from django.contrib.auth.management import create_permissions
def migrate_permissions(apps, schema_editor):
for app_config in apps.get_app_configs():
app_config.models_module = True
create_permissions(app_config, apps=apps, verbosity=0)
app_config.models_module = None
Django <= 1.9
see another answer for Django 1.10+
It's enough to call create_permissions:
from django.contrib.auth.management import create_permissions
apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None
The whole migration being something like this
# coding:utf-8
from django.db import migrations
from django.contrib.auth.models import Permission, Group
from django.contrib.auth.management import create_permissions
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
MODERATORS_PERMISSIONS = ['change_modelname', ]
def add_permissions(apps, schema_editor):
apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None
moderators_group = Group.objects.get_or_create(
name=settings.MODERATORS_GROUP)[0]
for codename in MODERATORS_PERMISSIONS:
permission = Permission.objects.get(codename=codename)
moderators_group.permissions.add(permission)
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('thisappname', '0001_initial'),
]
operations = [
migrations.RunPython(add_permissions),
]
And if you want something that will work on any version (or that will keep working when you upgrade):
from django.contrib.auth.management import create_permissions
version = django.VERSION
if version[0] >= 1 and django.VERSION[1] > 9:
for app_config in apps.get_app_configs():
app_config.models_module = True
create_permissions(app_config, apps=apps, verbosity=0)
app_config.models_module = None
else:
apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None
Trying to get an permission during migrations causes an exception(Permission matching query does not exist) in Django. It's an old problem in Django.
In 1.6 version I solved it via #int_ua's snippet but in 1.11 version it doesn't work(I'm not sure why).
I used this workaround in 1.11 version:
def _assign_group_permissions(permission_codenames, apps, group_name):
permission_list = []
Permission = apps.get_model('auth', 'Permission')
for permission_codename in permission_codenames:
for permission in Permission.objects.all():
if permission.codename == permission_codename:
permission_list.append(permission)
Group = apps.get_model('auth', 'Group')
group = Group.objects.get(name=group_name)
group.permissions.add(*permission_list)
Instead of Permission.objects.get(codename='your_code_name') it's possible to iterate over all permissions and choose suitable one by codename.
Django 3.2
Here is a version for Django 3.2, which you can run from the command line:
./manage.py fix_permissions
# app_label/management/commands/fix_permissions.py
from django.contrib.auth.models import Permission
from django.contrib.auth.management import create_permissions
from django.core.management.base import BaseCommand
from django.apps import apps
class Command(BaseCommand):
help = 'Recreate permissions from scratch'
def handle(self, *args, **options):
# Run this method via shell whenever any amendments in any of the tables is made
print("Deleting existing user permissions...")
Permission.objects.all().delete()
for app_config in apps.get_app_configs():
print(f"Adding user permissions for {app_config}...")
app_config.models_module = True
create_permissions(app_config, apps=apps, verbosity=0)
app_config.models_module = None
print("DONE.")

Django Cant access auth User, Group object in custom data migration

My migration file looks like this:
from __future__ import unicode_literals
from django.db import migrations
from project.tools import do_nothing
def create_can_receive_group(apps, schema_editor):
Group = apps.get_model("django.contrib.auth", 'Group')
# Group operation added here
class Migration(migrations.Migration):
dependencies = [
('poreceiving', '0004_auto_20150616_0846'),
('django.contrib.auth', '0006_require_contenttypes_0002')
]
operations = [
migrations.RunPython(create_can_receive_group, do_nothing),
]
Here I want to access Group object of django.contrib.auth.
I get the following exception.
*** LookupError: No installed app with label 'django.contrib.auth'.
I found somewhere that if we want to use other object which is not in app in which the migration is present then we should add latest migration of other app.
When I add django.contrib.auth latest migration to the dependency I get following :
django.db.migrations.graph.NodeNotFoundError: Migration poreceiving.0005_create_can_receive_group dependencies reference nonexistent parent node (u'django.contrib.auth', u'0006_require_contenttypes_0002')
Try something like this (look at the migrations.swappable_dependency part in the dependencies):
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations
from project.tools import do_nothing
def create_can_receive_group(apps, schema_editor):
Group = apps.get_model("auth", "Group")
# Group operation added here
class Migration(migrations.Migration):
dependencies = [
('poreceiving', '0004_auto_20150616_0846'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RunPython(create_can_receive_group, do_nothing),
]

django: data migrate permissions

I have a bunch of new permissions which I need to migrate. I tried doing it through data migration but complains about ContentType not being available.
Doing quick research I found out that ContentType table is populated after all the migrations applied.
I even tried using update_all_contenttypes() from from django.contrib.contenttypes.management import update_all_contenttypes
which causes migration to load data which is not consistent to the fixture.
What is the best way to migrate permission data in Django?
Here is a quick and dirty way to ensure all permissions for all apps have been created:
def add_all_permissions(apps=None, schema_editor=None):
from django.contrib.auth.management import create_permissions
if apps is None:
from django.apps import apps
for app_config in apps.get_app_configs():
app_config.models_module = True
create_permissions(app_config, verbosity=0)
app_config.models_module = None
class Migration(migrations.Migration):
dependencies = [('myapp', '0123_do_the_thing')]
operations = [
migrations.RunPython(add_all_permissions,
reverse_code=migrations.RunPython.noop)
# ...
]
NOTE: edited to include ruohola's excellent suggestion
There are 2 ways to solve this:
1) The ugly way:
Run manage.py migrate auth before your wanted migration
2) Recommended way:
from django.contrib.auth.management import create_permissions
def add_permissions(apps, schema_editor):
apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None
# rest of code here....
Here are steps for adding custom permissions to the User model:
First create a migration file, for example under your authentication application,
Here i named it 0002_permission_fixtures.py:
account (your authentication application)
|_migrations
|__ 0001_initial.py
|__ 0002_permission_fixtures.py
|__ __init__.py
Then adding your permission objects, as follow:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def forwards_func(apps, schema_editor):
# Get models that we needs them
user = apps.get_model("auth", "User")
permission = apps.get_model("auth", "Permission")
content_type = apps.get_model("contenttypes", "ContentType")
# Get user content type object
uct = content_type.objects.get_for_model(user)
db_alias = schema_editor.connection.alias
# Adding your custom permissions to User model:
permission.objects.using(db_alias).bulk_create([
permission(codename='add_sample', name='Can add sample', content_type=uct),
permission(codename='change_sample', name='Can change sample', content_type=uct),
permission(codename='delete_sample', name='Can delete sample', content_type=uct),
])
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '__latest__'),
]
operations = [
migrations.RunPython(
forwards_func,
),
]
To run this migration, first migrate contenttype model, and then migrate your application (here is account).
$ python manage.py migrate contenttypes
$ python manage.py migrate account

wsgi + django returning imports as NoneType

I have an application that works absolutely fine when run as ./manage.py runserver (or even runserver_plus), but when I deploy it to an apache2+wsgi instance, it breaks. The first model it tries to import (UserProfile) it appears to have imported the requested modules as NoneType.
So, a model like this (this is not exact code, it's not something I can paste onto a public site right now):
from django.db import models
from django.contrib.auth.models import User
from BlogEngine.categorisation.models import Category
from django.db.models.signals import post_save
from django.conf import settings
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext as _
import logging
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
category = models.ManyToManyField(Category, blank=True, related_name="category")
def __unicode__(self):
return "Profile for %s" % user.username
def update_categories(self):
"""Updates the categories list"""
pull_more = self.category_selection_max - self.category.count()
if pull_more == 0:
return self.category_selection
logging.debug("Drawing %s categories" % draw)
categories = Category.objects.filter(
is_live=True
).order_by("?")[:pull_more]
## More code under here ##
Returns:
'NoneType' object has no attribute 'debug'
at the line logging.debug("Drawing %s categories" % draw)
Commenting that out results in getting
'NoneType' object has no attribute 'objects'
at the line below it instead, and so on. Everything's definitely being imported, and it works fine under the dev server.
My WSGI file is:
import sys
import site
import os
vepath = '/home/aquarion/newsite/django/virtualenv/lib/python2.6/site-packages'
#prev_sys_path = list(sys.path)
## add the site-packages of our virtualenv as a site dir
site.addsitedir(vepath)
## add the app's directory to the PYTHONPATH
sys.path.append('/home/aquarion/newsite/django/')
# import from down here to pull in possible virtualenv django install
from django.core.handlers.wsgi import WSGIHandler
os.environ['DJANGO_SETTINGS_MODULE'] = 'BlogEngine.settings'
application = WSGIHandler()
Any ideas?
Solved it.
Not sure if it's a bug in something, but it ended up being an issue with using django's user profile stuff and having Admin model information in the models.py file. Once I moved all that into its own admin.py file, everything worked.
Still not sure what causes it exactly, but that's the solution.
(Solution reached via http://osdir.com/ml/DjangoUsers/2009-07/msg00090.html and its reply)