After seeing this post, I tried to create my own group at project setup with this migration :
from django.db import migrations
from django.contrib.auth.models import Group, Permission
def create_group(apps, schema_editor):
group, created = Group.objects.get_or_create(name='thing_managers')
if created:
add_thing = Permission.objects.get(codename='add_thing')
group.permissions.add(add_thing)
group.save()
class Migration(migrations.Migration):
dependencies = [
('main', '0002_auto_20160720_1809'),
]
operations = [
migrations.RunPython(create_group),
]
But I got the following error :
django.contrib.auth.models.DoesNotExist: Permission matching query does not exist.
Here is my model :
class Thing(models.Model):
pass
Why can't I do that? How could I solve this?
I use django 1.9.
Permissions are created in a post_migrate signal. They don't exist the first time migrations are run after a new model is added. It is probably easiest to run the post_migrate signal handler manually:
from django.contrib.auth.management import create_permissions
def create_group(apps, schema_editor):
for app_config in apps.get_app_configs():
create_permissions(app_config, apps=apps, verbosity=0)
group, created = Group.objects.get_or_create(name='thing_managers')
if created:
add_thing = Permission.objects.get(codename='add_thing')
group.permissions.add(add_thing)
group.save()
create_permissions checks for existing permissions, so this won't create any duplicates.
From this Django ticket, here's what worked for me in Django 3.0.4 and apparently will work in >=1.9:
from django.core.management.sql import emit_post_migrate_signal
def create_group(apps, schema_editor):
# Ensure permissions and content types have been created.
db_alias = schema_editor.connection.alias
emit_post_migrate_signal(2, False, db_alias)
# Now the content types and permissions should exist
Permission = apps.get_model('auth', 'Permission')
...
One solution is call the update_permissions command before try to append a permission
from django.core.management import call_command
def update_permissions(schema, group):
call_command('update_permissions')
operations = [
migrations.RunPython(update_permissions, reverse_code=migrations.RunPython.noop),
migrations.RunPython(create_group),
]
And as was commented don't import Group and Permission models use:
Group = apps.get_model("auth","Group")
Permission = apps.get_model("auth","Permission")
Related
I have set up a very basic Django website and I am looking to create a moderator group. I am unsure how to go about creating this programatically. Mainly, I am worried about which file I would need to put it in, and whether or not when I create it, will it show up on my admin site as a group. Otherwise, how else can I confirm it is made? Can I create groups from any app? For example, I have an app called 'users' which doesn't contain any models. Can I simply create my moderator group with example code for a generic group below in views.py or will that not work?
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from api.models import Project
new_group, created = Group.objects.get_or_create(name='new_group')
ct = ContentType.objects.get_for_model(Project)
permission = Permission.objects.create(codename='can_add_project',
name='Can add project',
content_type=ct)
new_group.permissions.add(permission)
Thanks for any help in advance!
0001_initial.py
# Generated by Django 3.2.6 on 2021-09-15 10:13
from django.db import models, migrations
def apply_migration(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
Group.objects.bulk_create([
Group(name=u'group1'),
Group(name=u'group2'),
Group(name=u'group3'),
])
def revert_migration(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
Group.objects.filter(
name__in=[
u'group1',
u'group2',
u'group3',
]
).delete()
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.RunPython(apply_migration, revert_migration)
]
I want add additional field to Order model. i have already forked order app.
below is the code added in Order model in forked_app Order app.
from django.utils import timezone
from oscar.apps.order.abstract_models import AbstractOrder
from oscar.apps.order.models import * # noqa isort:skip
from django.db import models
class Order(AbstractOrder):
status_update_time = models.CharField(max_length=30)
def save(self, *args, **kwargs):
self.status_update_time = timezone.now()
super(Order, self).save(*args, **kwargs)
Below is the error i get while migrations.
class Order(AbstractOrder):
NameError: name 'AbstractOrder' is not defined
Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x7f06737e6d08>
RuntimeError: Conflicting 'order' models in application 'order': <class 'oscar.apps.order.models.Order'> and <class 'forked_apps.order.models.Order'>.
from django.utils import timezone
from oscar.apps.order.abstract_models import AbstractOrder
from django.db import models
class Order(AbstractOrder):
status_update_time = models.CharField(max_length=30)
def save(self, *args, **kwargs):
self.status_update_time = timezone.now()
super(Order, self).save(*args, **kwargs)
at the end of the models.py file
from oscar.apps.order.models import *
then try to do makemigrations and then migrate
In order to customize models, views and urls, you need to fork an Oscar core app in which model/view resides. Then you should be able to override any model/view classes.
Steps to fork/customize an app:
If you are forking an Oscar app for the first time, then you have to create a root apps-folder in which all your forked apps will exists:
$ mkdir yourappsfolder
$ touch yourappsfolder/init.py
Create a python module with the same ‘app-label’ as the Oscar app:
Ex: Customising oscar.apps.catalogue app
$ mkdir yourappsfolder/catalogue
$ touch yourappsfolder/catalogue/__init__.py
If the Oscar app has a models.py, then you have to create a models.py file in your local app.
your custom models go here
from oscar.apps.catalogue.models import *
NOTE: To customise Oscar’s models, you must add your custom one before importing Oscar’s models. Then, your models file will have two models with the same name within an app, Django will only use the first one.
Ex: To add a active field to the product model:
# yourappsfolder/catalogue/models.py
from django.db import models
from oscar.apps.catalogue.abstract_models import AbstractProduct
class Product(AbstractProduct):
active = models.BooleanField(default=False)
from oscar.apps.catalogue.models import *
Create an ‘admin.py’ file in your local app.
yourappsfolder/catalogue/admin.py
from oscar.apps.catalogue.admin import *
Then copy the ‘migrations’ directory from oscar/apps/catalogue and put it into your new local catalogue app.
Added it as Django app by replacing Oscar’s app with your own in INSTALLED_APPS.
settings.py
from oscar import get_core_apps
INSTALLED_APPS = [
...,
# all your non-Oscar apps
] + get_core_apps(['yourappsfolder.catalogue'])
NOTE: get_core_apps([]) will return a list of Oscar core apps or else if you give a list of your custom apps, they will replace the Oscar core apps.
Finally, create migrations using ‘makemigrations’ management command and apply the migrations by using ‘migrate catalogue’ management command. Then, you can see that a new column is been added to the product model.
My goal is to have one dummyuser in the database. I already wrote the following function
def get_test_user():
user, created = get_user_model.objects.get_or_create(username=TESTUSER_USERNAME)
user.set_password(TESTUSER_PASSWORD)
user.save
But where and how should I call it to achieve my goal. I am aware that testing django will create its own database and that there are functions for creating test users. But this should actually run in the production database as well.
You can create data migration, for example:
from django.contrib.auth.hashers import make_password
from django.db import migrations
def create_user(apps, schema_editor):
User = apps.get_registered_model('auth', 'User')
user = User(
username='user',
email='user#mail.com',
password=make_password('pass'),
is_superuser=False,
is_staff=False
)
user.save()
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial')
]
operations = [
migrations.RunPython(create_user),
]
I was trying to create a custom permission in a migration, however after running migrate, the permission was not created in the permission table. Could someone point out what the error was?
Also I am not sure what I should use as the related model for ContentType as the permission is used for restricting users that can view a page which shows summary of users on the site.
Any help will be greatly appreciated, thanks.
def add_view_aggregated_data_permissions(apps, schema_editor):
ContentType = apps.get_model('django', 'ContentType')
Permission = apps.get_model('auth', 'Permission')
content_type = ContentType.objects.get(app_label='auth', model='user')
permission = Permission.objects.create(codename='can_view_data',
name='Can view data',
content_type=content_type)
I would recommend you to use the standard way to use custom permissions as described in the Django documentation. You will avoid many issues altogether.
To create custom permissions for a given model object, use the permissions model Meta attribute.
This example model creates a custom permission:
class MyModel(models.Model):
...
class Meta:
permissions = (
('view_data', "Can see available data"),
)
The only thing this does is create those extra permissions when you run manage.py migrate. Your code is in charge of checking the value of these permissions when a user is trying to access the functionality provided by the application...
Then you can use the permission_required decorator with your view to check for the specific permission:
from django.contrib.auth.decorators import permission_required
#permission_required('myapp.view_data')
def my_view(request):
...
I wanted to created a custom permission (read) for all app models. I did this two steps:
Create an extended permission from DjangoModelPermissions:
class DjangoModelPermissionsExtended(DjangoModelPermissions):
"""
"""
perms_map = {
'GET': ['%(app_label)s.read_%(model_name)s'],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
Put it in each view I want to have read permission:
class ExampleViewSet(viewsets.ModelViewSet):
permission_classes = (
DjangoModelPermissionsExtended,
)
Create a django command customread.py:
from django.core.management.base import BaseCommand, CommandError
from project.app import models as app_models
from django.db import models
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
import inspect
class Command(BaseCommand):
help = 'Create the read permission to app models'
def handle(self, *args, **options):
for name, obj in inspect.getmembers(app_models):
if inspect.isclass(obj) and issubclass(obj, models.Model):
try:
self.add_canread(obj)
self.stdout.write(self.style.SUCCESS(
'created permission for %s' % obj
))
except Exception as e:
self.stdout.write(self.style.ERROR(
'Permission already exists for %s' % obj
))
def add_canread(self, object_class):
"""This a function that can be executed in order to create
new permissions (read view) to a class in DB.
"""
if inspect.isclass(object_class):
content_type = ContentType.objects.get_for_model(object_class)
permission = Permission.objects.create(
codename='read_{}'.format(object_class._meta.model_name),
name='Can view {}'.format(object_class.__name__),
content_type=content_type,
)
else:
msg = "The object is not a class"
print(msg)
Execute it after doing migrations:
python manage.py customread
As of django 1.8 and built-in migrations this is very painless.
All you need to do is add the permissions you want to the relevant
model
Run makemigration
./manage.py makemigrations
run the migration created in the step above
./manage.py migrate
I am trying to add view_amodel permission to my models. I decided to add the permission after migration. So I made following approach.
At an_app/init.py
from an_app.apps import MyAppConfig
default_app_config = MyAppConfig
At an_app/apps.py
from django.apps import AppConfig
from django.db.models.signals import post_migrate
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission
def add_view_permissions(sender, **kwargs):
"""
This syncdb hooks takes care of adding a view permission too all our
content types.
"""
# for each of our content types
for content_type in ContentType.objects.all():
# build our permission slug
codename = "view_%s" % content_type.model
# if it doesn't exist..
if not Permission.objects.filter(content_type=content_type, codename=codename):
# add it
Permission.objects.create(content_type=content_type,
codename=codename,
name="Can view %s" % content_type.name)
print "Added view permission for %s" % content_type.name
class MyAppConfig(AppConfig):
def ready(self):
post_migrate.connect(add_view_permissions, sender=self)
When I do python manage.py migrate, I get following error,
AttributeError: type object 'MyAppConfig' has no attribute 'rpartition'
How to solve it.
The reference to the AppConfig in the app's __init__.py is supposed to be a string, not the class itself.
Specify
default_app_config = 'an_app.apps.MyAppConfig'
and remove the import.
See the documentation.