How to reference generated permissions in Django 1.7 migrations - python

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.

Related

Django Guardian with Data Migration (adding Anonymous user to public group)

I'm trying to perform a data migration in an app. I would like to add the anonymous user (the django-guardian AnonymousUser) to a public group. Somehow the user does not exist when i'm running test (test database migrations fail with user matching query does not exist). It does work with my development database. Thanks in advance.
As it seems that the anonymous user is created on management init, I tried to import the module in apply_migration without success.
# Generated by Django 4.1 on 2022-10-19 12:11
from django.db import migrations
from guardian.utils import get_anonymous_user
from recipebook.settings import PUBLIC_GROUP_NAME
from django.contrib.auth.models import Group
def apply_migration(apps, schema_editor):
import guardian.management.__init__
public_group = Group.objects.get(
name=PUBLIC_GROUP_NAME
)
user = get_anonymous_user()
user.groups.add(public_group)
def revert_migration(apps, schema_editor):
public_group = Group.objects.get(
name=PUBLIC_GROUP_NAME
)
user = get_anonymous_user()
user.groups.remove(public_group)
class Migration(migrations.Migration):
dependencies = [
('recipebook', '0031_recipenutrition'),
('guardian', '0001_initial'),
]
operations = [
migrations.RunPython(apply_migration, revert_migration)
]

Migration references dynamic storage inconsistent

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?

View new column conditioned on other columns in Django

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

How can I add a new field in model when I already have a datamigration in Django?

I have a migrations come from the work of another person, and I'm in on a code base controlled by the version too. I created a migration file after adding a new field but the problem is in the data migration (0003_datamigration_initial_service_configuration.py)that is created before through the function get_or_create () is executed in the head of the list while my migration file data in the end I should not change a migration already developed.
This is my list of migrations
0001_initial.py
0002_datamigration_initial_users_list.py
0003_datamigration_initial_mymodel.py
...
0015.addnewfield
0003_datamigration_initial_mymodel.py:
from __future__ import unicode_literals
from django.db import migrations
from ..models import MyModel
def create_initial_mymodel(apps, schema_editor):
current_product_type, created = MyModel.objects.get_or_create()
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_datamigration_initial_users_list'),
]
operations = [
migrations.RunPython(create_initial_mymodel),
]
Rhe error is no such column named new_field
How can I fix the problem without editing any data migrations?
You should use apps.get_model to get the MyModel model, instead of importing it directly.
def create_initial_mymodel_forward(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
current_product_type, created = MyModel.objects.get_or_create()
Using apps.get_model will load a historical version of the model with the correct fields.

adding custom permission through django-admin, while server is running

In Django-admin, Is it possible to make feature so that admin can create/edit/delete certain permission through django-admin while the server is running?
In django-admin I want the permission can be listed, with edit create and delete feature
Using permission in Meta subclass of models class will create custom permission through migrate script.
taken from https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#custom-permissions
class Task(models.Model):
...
class Meta:
permissions = (
("view_task", "Can see available tasks"),
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
)
This will insert values on auth_permission (and django_content_type)
But that requires database migrations, which means unlikely be done by user (Admin) but by the developer instead. And I believe it is necessary for application to be able to manage permission, creating, editing, deleting while the server is running.
So what is the best practice to make this functionality with Django? Should I keep using migrate & create them all in each model and to reload server everytime we implement new permission or feature in general? thankyou
You can register the Permission model to admin view:
from django.contrib.auth.models import Permission
from django.contrib import admin
admin.site.register(Permission)
The code can be anywhere where it gets executed, but admin.py of your application could be an intuitive place to stick it in. After this, you will be able to view, edit and remove the permissions.
You also need to select_related because it will result in a bunch of SQL queries
from django.contrib import admin
from django.contrib.auth.models import Permission
#admin.register(Permission)
class PermissionAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related('content_type')

Categories

Resources