I want specific field in model to use either S3 storage (if settings has AWS_ACCESS_KEY_ID) or fallback to default_storage. I'm using django-storages.
To implement this, in models.py:
from django.db import models
from django.conf import settings
from .storage_backends import variativeStorage
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
photo = models.ImageField(upload_to='/public', default='dummy-img.png', storage=variativeStorage())
storage_backends.py:
from storages.backends.s3boto3 import S3Boto3Storage
from django.core.files.storage import default_storage
from django.conf import settings
def variativeStorage():
"""Return S3Boto3Storage if access key is defined.
Fallback to default storage.
"""
if hasattr(settings, 'AWS_ACCESS_KEY_ID') and settings.AWS_ACCESS_KEY_ID:
return S3Boto3Storage()
return default_storage
It works great, but the problem is in migrations - it evaluates field storage on which django settings it currently has. So in case we don't have AWS_ACCESS_KEY_ID, makemigrations will do:
operations = [
migrations.AlterField(
model_name='profile',
name='photo',
field=models.ImageField(default='dummy-img.png', upload_to='/public'),
),
]
and in case it has:
operations = [
migrations.AlterField(
model_name='profile',
name='photo',
field=models.ImageField(default='dummy-img.png', storage=storages.backends.s3boto3.S3Boto3Storage(), upload_to='/public'),
),
]
So running server on production will trigger warning for missing migrations:
remote: Running migrations:
remote: No migrations to apply.
remote: Your models have changes that are not yet reflected in a migration, and so won't be applied.
remote: Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
Is there a consistent way of doing dynamic storage for field? How can I make lazy-evaluation of variativeStorage or maybe remove migration generation for that particular field?
With Django 1.11, I am trying to add a new column that is conditioned on other columns (as shown in the below image) and view it at front-end. This link is the closest example but I would like to implement it in Django. How can we do this?
I suggest implement this action on the migration file:
After you changed model, execute ./manage.py makemigrations
Open new migration file in editor, content of this file maybe is similar as below:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.AddField('MyModel', 'A_islargerthan_B', models.BooleanField(default=False)),
]
Now you must inject your updater code by using migrations.RunPython :
from django.db import migrations, models
def update_A_islargerthan_B(apps, schema_editor):
MyModel = apps.get_model('my_app', 'MyModel')
for obj in MyModel.objects.all():
obj.A_islargerthan_B = obj.column_A > obj.column_B
obj.save()
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.AddField('MyModel', 'A_islargerthan_B', models.BooleanField(default=False)),
migrations.RunPython(update_A_islargerthan_B),
]
Run ./manage.py migrate
Read more about Django migrations
guys im trying to remove default django permissions i'll never use in my project but with no success. When I make migration it states that migration is made with success but there's no effect, like it skipped the function. I'm pretty sure code is ok because i tested it in shell. Any ideas?
Here's code for migration:
from django.db import migrations
def remove_redundant_permissions(apps, schema_editor):
Permission = apps.get_model('auth.Permission')
app_labels = ['admin', 'reversion', 'contenttypes', 'sessions', 'sites']
Permission.objects.filter(content_type__app_label__in=app_labels).delete()
class Migration(migrations.Migration):
dependencies = [
('users', '0014_auto_20160808_0738'),
]
operations = [
migrations.RunPython(remove_redundant_permissions),
]
Django's permessions are generated with post_migrate signal. This means even if you delete them in migration they will be regenerated after migration completes.
Here is code from django
# django/contrib/auth/apps.py
from django.apps import AppConfig
from django.contrib.auth.checks import check_user_model
from django.core import checks
from django.db.models.signals import post_migrate
from django.utils.translation import ugettext_lazy as _
from .management import create_permissions
class AuthConfig(AppConfig):
name = 'django.contrib.auth'
verbose_name = _("Authentication and Authorization")
def ready(self):
post_migrate.connect(create_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions")
checks.register(check_user_model, checks.Tags.models)
As you see auth app has this AppConfig which regenerates permissions with create_permissions function.
Why do you want to delete default django permissions? Do they block you from doing something?
I'm trying to create a auth.Group with permissions automaticly with migrations. My problem is that when I run migrate on empty database the migration that tries to attach permission to the group can't find the permission. If I target an earlier migration so that migrate exits without an error the permissions appear into the database and after that the migration code can find the permission. So what can I do so that the migration can reference a permission which is created in an earlier migration when the migrations are run back to back?
def load_data(apps, schema_editor):
Permission = apps.get_model('auth', 'Permission')
Group = apps.get_model('auth', 'Group')
can_add = Permission.objects.get(codename='add_game')
developers = Group.objects.create(name='Developer')
developers.permissions.add(can_add)
developers.save()
class Migration(migrations.Migration):
dependencies = [
('myApp', '0004_game'),
]
operations = [
migrations.RunPython(load_data),
]
The game model is created in an earlier migration. This code causes always an error stating that Permission matching query does not exist when I run it with other migrations on an empty database.
I'm using python 3.4 with django 1.7.2
Oh. 4 years later...
To create permissions Django uses post_migrate signal.
Therefore, when running all migrations at a time, permissions do not yet exist.
Therefore, you can take out your function, for example, in the management command.
However, you can still do it like this:
from django.contrib.auth.management import create_permissions
APPS = [
...your app labels
]
def create_applications_permissions():
for app in APPS:
app_config = django_apps.get_app_config(app)
create_permissions(app_config)
def load_data(apps, schema_editor):
create_applications_permissions()
Permission = apps.get_model('auth', 'Permission')
Group = apps.get_model('auth', 'Group')
can_add = Permission.objects.get(codename='add_game')
developers = Group.objects.create(name='Developer')
developers.permissions.add(can_add)
developers.save()
class Migration(migrations.Migration):
dependencies = [
('myApp', '0004_game'),
]
operations = [
migrations.RunPython(load_data),
]
And to create permissions do not use apps passed to the migration. Will not pass the check in create_permissions:
if not app_config.models_module:
return
But you have to be careful.
I hope someone will be useful.
I'm developing a Django app that will have two administration backends. One for daily use by "normal" users and the default one for more advanced tasks and for the developers.
The application uses some custom permissions but none of the default ones. So I'm currently looking for a way to remove the default permissions, or at least a way to hide them from the "daily" admin backend without large modifications.
UPDATE: Django 1.7 supports the customization of default permissions
Original Answer
The following is valid for Django prior to version 1.7
This is standard functionality of the auth contrib application.
It handles the post_syncdb signal and creates the permissions (the standard 3: add, change, delete, plus any custom ones) for each model; they are stored in the auth_permission table in the database.
So, they will be created each time you run the syncdb management command
You have some choices. None is really elegant, but you can consider:
Dropping the auth contrib app and provide your own authentication backend.
Consequences -> you will lose the admin and other custom apps built on top of the auth User model, but if your application is highly customized that could be an option for you
Overriding the behaviour of the post_syncdb signal inside the auth app (inside \django\contrib\auth\management__init__.py file)
Consequences -> be aware that without the basic permissions the Django admin interface won't be able to work (and maybe other things as well).
Deleting the basic permissions (add, change, delete) for each model inside the auth_permission table (manually, with a script, or whatever).
Consequences -> you will lose the admin again, and you will need to delete them each time you run syncdb.
Building your own Permission application/system (with your own decorators, middlewares, etc..) or extending the existing one.
Consequences -> none, if you build it well - this is one of the cleanest solutions in my opinion.
A final consideration: changing the contrib applications or Django framework itself is never considered a good thing: you could break something and you will have hard times if you will need to upgrade to a newer version of Django.
So, if you want to be as clean as possibile, consider rolling your own permission system, or extending the standard one (django-guardian is a good example of an extension to django permissions). It won't take much effort, and you can build it the way it feels right for you, overcoming the limitations of the standard django permission system. And if you do a good work, you could also consider to open source it to enable other people using/improving your solution =)
I struggled with this same problem for a while and I think I've come up with a clean solution. Here's how you hide the permissions for Django's auth app:
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django import forms
from django.contrib.auth.models import Permission
class MyGroupAdminForm(forms.ModelForm):
class Meta:
model = MyGroup
permissions = forms.ModelMultipleChoiceField(
Permission.objects.exclude(content_type__app_label='auth'),
widget=admin.widgets.FilteredSelectMultiple(_('permissions'), False))
class MyGroupAdmin(admin.ModelAdmin):
form = MyGroupAdminForm
search_fields = ('name',)
ordering = ('name',)
admin.site.unregister(Group)
admin.site.register(MyGroup, MyGroupAdmin)
Of course it can easily be modified to hide whatever permissions you want. Let me know if this works for you.
A new feature introduced in Django 1.7 is the ability to define the default permissions. As stated in the documentation if you set this to empty none of the default permissions will be created.
A working example would be:
class Blar1(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique = True, blank = False, null = False, verbose_name= "Name")
class Meta:
default_permissions = ()
ShadowCloud gave a good rundown. Here's a simple way to accomplish your goal.
Add these line in your admin.py:
from django.contrib.auth.models import Permission
admin.site.register(Permission)
You can now add/change/delete permissions in the admin. Remove the unused ones and when you have what you want, go back and remove these two lines from admin.py.
As was mentioned by others, a subsequent syncdb will put everything back.
Built on top of the solution by #pmdarrow, I've come up with a relatively clean solution to patch the Django admin views.
See: https://gist.github.com/vdboor/6280390
It extends the User and Group admin to hide certain permissions.
You can't easily delete those permissions (so that syncdb won't put them back), but you can hide them from the admin interface. The idea is, as described by others, to override the admin forms but you have to do this for both users and groups.
Here is the admin.py with the solution:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User, Group
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.auth.forms import UserChangeForm
#
# In the models listed below standard permissions "add_model", "change_model"
# and "delete_model" will be created by syncdb, but hidden from admin interface.
# This is convenient in case you use your own set of permissions so the list
# in the admin interface wont be confusing.
# Feel free to add your models here. The first element is the app name (this is
# the directory your app is in) and the second element is the name of your model
# from models.py module of your app (Note: both names must be lowercased).
#
MODELS_TO_HIDE_STD_PERMISSIONS = (
("myapp", "mymodel"),
)
def _get_corrected_permissions():
perms = Permission.objects.all()
for app_name, model_name in MODELS_TO_HIDE_STD_PERMISSIONS:
perms = perms.exclude(content_type__app_label=app_name, codename='add_%s' % model_name)
perms = perms.exclude(content_type__app_label=app_name, codename='change_%s' % model_name)
perms = perms.exclude(content_type__app_label=app_name, codename='delete_%s' % model_name)
return perms
class MyGroupAdminForm(forms.ModelForm):
class Meta:
model = Group
permissions = forms.ModelMultipleChoiceField(
_get_corrected_permissions(),
widget=admin.widgets.FilteredSelectMultiple(('permissions'), False),
help_text = 'Hold down "Control", or "Command" on a Mac, to select more than one.'
)
class MyGroupAdmin(GroupAdmin):
form = MyGroupAdminForm
class MyUserChangeForm(UserChangeForm):
user_permissions = forms.ModelMultipleChoiceField(
_get_corrected_permissions(),
widget=admin.widgets.FilteredSelectMultiple(('user_permissions'), False),
help_text = 'Hold down "Control", or "Command" on a Mac, to select more than one.'
)
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
admin.site.unregister(Group)
admin.site.register(Group, MyGroupAdmin)
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
If you are creating your own user management backend and only want to show your custom permissions you can filter out the default permissions by excluding permission with a name that starts with "Can".
WARNING:
You must remember not to name your permissions starting with "Can"!!!!
If they decide to change the naming convention this might not work.
With credit to pmdarrow this is how I did this in my project:
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import Permission
from django.contrib import admin
class UserEditForm(UserChangeForm):
class Meta:
model = User
exclude = (
'last_login',
'is_superuser',
'is_staff',
'date_joined',
)
user_permissions = forms.ModelMultipleChoiceField(
Permission.objects.exclude(name__startswith='Can'),
widget=admin.widgets.FilteredSelectMultiple(_('permissions'), False))
If you want to prevent Django from creating permissions, you can block the signals from being sent.
If you put this into a management/init.py in any app, it will bind to the signal handler before the auth framework has a chance (taking advantage of the dispatch_uid debouncing).
from django.db.models import signals
def do_nothing(*args, **kwargs):
pass
signals.post_syncdb.connect(do_nothing, dispatch_uid="django.contrib.auth.management.create_permissions")
signals.post_syncdb.connect(do_nothing, dispatch_uid="django.contrib.auth.management.create_superuser")