I want to create groups in django programmatically, but not in a view, but rather in something like model (for example using migrations). How to do it? There's no information about it in google and docs (at least not here: https://docs.djangoproject.com/en/1.7/topics/auth/default/#groups)
Okay, it seems you're using Django 1.7's new migrations system. This is similar to but not exactly like South.
A migration that involves altering the data in the tables is a data migration, and you typically need to write Python code to do the migration.
From the Django docs, there's this example:
# -*- coding: utf-8 -*-
from django.db import models, migrations
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model("yourappname", "Person")
for person in Person.objects.all():
person.name = "%s %s" % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
Note that the code to run during the migration is in the combine_names function, which is called by the migrations.RunPython(combine_names) entry in the operations list of the migration. Your migration should do its group creation in a function like that, along with whatever other data migration is needed.
You should probably use a line like
Group = apps.get_model("auth", "Group")
my_group, created = Group.objects.get_or_create(name='group1')
to create your groups, in case there is already a group of that name in the table.
Don't put code to run during a migration into the root level of the Python file; if you do so, it will be run every time that migration is imported, for example, every time you run ./manage.py runserver.
P.S. You need to put your migrations.RunPython entry at the right point in the operations list; it won't work if you put it after an operation that deletes a table it needs, for example.
Groups are just like any other Django model. You can create them as you would anything else.
my_group = Group.objects.create(name='group1')
Related
I want my application to have default data such as user types.
What's the most efficient way to manage default data after migrations?
It needs to handle situations such as, after I add a new table, it adds the default data for it.
You need to create an empty migration file and Do your stuff in operations block, as explained in docs.
Data Migrations
As well as changing the database schema, you can also use migrations to change the data in the database itself, in conjunction with the schema if you want.
Now, all you need to do is create a new function and have RunPython use it
Docs explains this with an example to show ,how to communicate with your models.
From Docs
To create an empty migration file,
python manage.py makemigrations --empty yourappname
And this is the example how to update a newly added field.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model("yourappname", "Person")
for person in Person.objects.all():
person.name = "%s %s" % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
initial = True
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
The accepted answer is fine. But, since OP asked the question in the context of adding new rows and not updating existing entries. Here is the code snippet for adding new entries :
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('loginmodule', '0002_login_avatar'),
]
def insertData(apps, schema_editor):
Login = apps.get_model('loginmodule', 'Login')
user = Login(name = "admin", login_id = "admin", password = "password", email = "admin#pychat.com", type = "Admin", avatar="admin.jpg")
user.save()
operations = [
migrations.RunPython(insertData),
]
Update:
most users are looking for data migration as suggested by #durdenk in https://stackoverflow.com/a/39742847/3627387. But what OP was asking is about a way to add data after migrations, that is why this is accepted answer.
Original answer:
I think what you are looking for is fixtures https://docs.djangoproject.com/en/1.10/howto/initial-data/
From docs
It’s sometimes useful to pre-populate your database with hard-coded data when you’re first setting up an app. You can provide initial data via fixtures.
Also read this https://code.djangoproject.com/wiki/Fixtures
Answer is given above just to show how to insert new rows to the table.
from django.db import migrations, models
from yourapp.models import <yourmodel>
def combine_names(apps, schema_editor):
obj = <yourmodel>(arrib=value)
obj.save()
For example let's say you have model Person
person = Person(first_name='raj', last_name='shah')
person.save()
I'm trying to rename a model and I would like to write the migration in the way that it doesn't depend on the old name still present while it being applied. Can I somehow get data from a database table that no longer has a model in my migration code?
Details:
I have a Region model that I want to move into a more generic GeoObject model and remove from the models.py. If I write my migration code that creates GeoObjects from existing Regions with from models import Region I'll have to keep Region model until my main database will migrate. But I'd like to write a migration so that it doesn't depend on Region model being present, just check that the database table exists and use it. Is it possible to do it using Django instruments, without depending on a specific database type if possible?
Yes, you can.
But first of all, you really shouldn't import any model inside migration.
Take look at RunPython operation, that will allow you to run any python code inside your migration. RunPython will pass to your function 2 parameters: apps and schema_editor. First parameter contains structure of your models at stage of applying that migration, so if actual removing of model is later on that migration, you can still access that model using apps passed into function.
Let's say your model looked like this:
class SomeModel(models.Model):
some_field = models.CharField(max_length=32)
Now you're deleting that model, automatically created migration will contain:
class Migration(migrations.Migration):
dependencies = [
('yourapp', '0001_initial'), # or any other dependencies
]
operations = [
migrations.DeleteModel(
name='Main',
),
]
You can modify that migration by injecting RunPython just above DeleteModel operation:
operations = [
migrations.RunPython(
move_data_to_other_model,
move_data_back, # for backwards migration - if you won't ever want to undo this migration, just don't pass that function at all
),
migrations.DeleteModel(
name='SomeModel',
),
]
and creating 2 functions before Migration class:
def move_data_to_other_model(apps, schema_editor):
SomeModel = apps.get_model('yourapp', 'SomeModel')
for something in SomeModel.objects.all():
# do your data migration here
o = OtherModel.objects.get(condition=True)
o.other_field = something.some_field
def move_data_back(apps, schema_editor):
SomeModel = apps.get_model('yourapp', 'SomeModel')
for something in OtherModel.objects.all():
# move back your data here
SomeModel(
some_field=something.other_field,
).save()
It doesn't matter that your model is no longer defined in models.py, django can rebuild that model based on migration history. But remember: save method from your models (and other customized methods) won't be called in migrations. Also any pre_save or post_save signals won't be triggered.
suddenly, my page got so many users in db that the a filter for email over the auth_user table almost failing because of the extremely big number of users.
Since the table comes built-in, I need to add db_index=True to columns in this table, any idea how to do this?
One quick and easy way would be to manually add the index using RunSQL in a migration.
operations = [
migrations.RunSQL("CREATE INDEX..."),
]
It's not very elegant. For one thing, the migration will be for a different app (since you don't control the auth migrations). For another, the schema will technically be out of sync with the database. However, I don't think there are any negative consequences in this case, since Django doesn't do anything with db_index other than create the index.
One possibility is to substitute the user model with a custom one, which will have proper indices and any other field that you require. There is extensive documentation on Django docs: Substituting a custom User model on how to achieve this. This is how I did it on a particular case with a similar issue.
Another possibility is to extend the user model, which could have a particular field repeated from the original model, on which there is an index. Disclaimer: I am genuinely against that for obvious reasons, but I have seen this happening, as this approach is easier to code than the first. This would be very bad though if there are many fields.
This is a good question imo. I would love to know if there is another possibility which I miss.
I had the same problem, but with an additional twist--- I already had an index created by South in my table. So if I added a RunSQL("Create index") to my migration, it would add a second index to my database. But at the same time, if I don't include some create index action in the migrations, then I won't be able to properly spin up new databases.
Here's my solution, some python code to check for the existence of an index using some of the private-ish methods in schema_editor
project/appname/migrations/0002.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db.migrations import RunPython
def forward__auth_user__email__index(apps, schema_editor):
auth_user = apps.get_model("auth", "User")
target_column_to_index = 'email'
se = schema_editor
# if there aren't any indexes already, create one.
index_names = se._constraint_names(auth_user, [target_column_to_index], index=True)
if len(index_names) == 0:
se.execute(se._create_index_sql(auth_user, [auth_user._meta.get_field('email')]))
def reverse__auth_user__email__index(apps, schema_editor):
auth_user = apps.get_model("auth", "User")
target_column_to_index = 'email'
se = schema_editor
# delete any indexes for this table / column.
index_names = se._constraint_names(model, [target_column_to_index], index=True)
for index_name in index_names:
se.execute(se._delete_constraint_sql(se.sql_delete_index, auth_user, index_name))
class Migration(migrations.Migration):
dependencies = [
('frontend', '0001_initial'),
]
operations = [
RunPython(forward__auth_user__email__index, reverse_code=reverse__auth_user__email__index)
]
I want to create two groups once per project's life. So, I read about AppConfig
And I created core.appconfig.py:
from django.apps import AppConfig
from django.contrib.auth.models import Group
class RolesConfig(AppConfig):
name = 'roles_config'
verbose_name = 'Roles configuration'
def create_roles(self):
driver = Group.objects.create(name='driver')
manager = Group.objects.create(name='manager')
driver.save()
manager.save()
And the in settings.py: default_app_config = 'core.appconfig.RolesConfig'
But when I run server and go to the admin page, there are no groups. Why? When is AppConfig called?
Consider using a data migration:
Create an empty migration file with manage.py makemigrations <app_name> --empty.
Create a function that adds the default values to the database.
def create_roles(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
driver = Group.objects.create(name='driver')
manager = Group.objects.create(name='manager')
Add a RunPython operation to you migrations:
class Migration(migrations.Migration):
operations = [
migrations.RunPython(create_roles),
]
Automatic loading of data fixtures has been deprecated in favour of data migrations.
I consider #Leistungsabfall answer to be correct, apart from that: don't do this. App config was not built for this purpose, instead you should create fixtures: https://docs.djangoproject.com/en/1.8/howto/initial-data/ .
App config is run every time you run the application so it would not really work.
There are several things wrong here:
Make sure the path to appconfig.py is myapp/appconfig.py.
Make sure that name is your Django application name (e.g. myapp).
Rename create_roles(self) to ready(self).
In myapp/__init__.py (create this file if it doesn't exist) add this line:
default_app_config = 'myapp.appconfig.RolesConfig'
Remove driver.save() and manager.save(), they are redundant because create() already does save the objects in the database.
(Replace myapp with your Django application name.)
I use Django and South for my database. Now I want to add a new Model and a field in an existing model, referencing the new model. For example:
class NewModel(models.Model):
# a new model
# ...
class ExistingModel(models.Model):
# ... existing fields
new_field = models.ForeignKey(NewModel) # adding this now
Now South obviously complains that I added a non-null field and asks me to enter a one-off value. But what I really want is to create a new NewModel instance for every existing ExistingModel instance, thus fulfilling the database requirements. Is that possible somehow?
The easiest way to do this is to write a schema migration that makes the column change, and then write a datamigration to correctly fill in the value. Depending on the database you're using you'll have to do this in slightly different ways.
Sqlite
For Sqlite, you can add a sentinel value for the relation and use a datamigration to fill it in without any issue:
0001_schema_migration_add_foreign_key_to_new_model_from_existing_model.py
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
db.add_column('existing_model_table', 'new_model',
self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['appname.new_model']), keep_default=False)
0002_data_migration_for_new_model.py:
class Migration(DataMigration):
def forwards(self, orm):
for m in orm['appname.existing_model'].objects.all():
m.new_model = #custom criteria here
m.save()
This will work just fine, with no issues.
Postgres and MySQL
With MySql, you have to give it a valid default. If 0 isn't actually a valid Foreignkey, you'll get errors telling you so.
You could default to 1, but there are instances where that isn't a valid foreign key (happened to me because we have different environments, and some environments publish to other databases, so the IDs rarely match up (we use UUIDs for cross-database identification, as God intended).
The second issue you get is that South and MySQL don't play well together. Partially because MySQL doesn't have the concept of DDL transactions.
In order to get around some issues you will inevitably face (including the error I mentioned above and from South asking you to mark orm items in a SchemaMigration as no-dry-run), you need to change the above 0001 script to do the following:
0001_schema_migration_add_foreign_key_to_new_model_from_existing_model.py
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
id = 0
if not db.dry_run:
new_model = orm['appname.new_model'].objects.all()[0]
if new_model:
id = new_model.id
db.add_column('existing_model_table', 'new_model',
self.gf('django.db.models.fields.related.ForeignKey')(default=id, to=orm['appname.new_model']), keep_default=False)
And then you can run the 0002_data_migration_for_new_model.py file as normal.
I advise using the same example above for Postgres and for MySql. I don't remember any issues offhand with Postgres with the first example, but I'm certain the second example works for both (tested).
You want a data migration to supplement your schema migration in this scenario.
South has a nice step by step tutorial on how to achieve this in the docs, here.
It's not uncommon in South to have the desired outcome spread over two or three schema/data migrations as its not always possible to do it in one big hit (sometimes depends on the underlying db if it will tolerate adding a non null column with no default). So in this case you might add a schema migration that has a default, then a data migration with your object manipulation then a final schema migration.