django-registration-redux and spurious migration from DEFAULT_AUTO_FIELD of BigAutoField - django

I installed django-registration-redux 2.11 into Django 4.1.6.
When I run makemigrations, it makes a migration to change an id field to BigAutoField:
# Generated by Django 4.1.6 on 2023-02-03 19:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registration", "0005_activation_key_sha256"),
]
operations = [
migrations.AlterField(
model_name="registrationprofile",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
]
I suspect this is because DEFAULT_AUTO_FIELD is now BigAutoField as of Django 3.2, see here.
However, it's creating a migration in my virtualenv, not under source code control:
$ ./manage.py makemigrations
Migrations for 'registration':
.venv/lib/python3.10/site-packages/registration/migrations/0006_alter_registrationprofile_id.py
- Alter field id on registrationprofile
So that's not gonna work well.
I don't want to change my DEFAULT_AUTO_FIELD to AutoField. I just want to change it for django-registration-redux I guess?
I see that registration already has default_auto_field = 'django.db.models.AutoField', so I'm puzzled as to why Django is making this migration.
I tried adding my own registration/apps.py into source code control with default_auto_field as AutoField, and that didn't work.
Suggestions?
I might just have to move to django-allauth or back to django-registration, as they suggest.

I see that registration already has default_auto_field = 'django.db.models.AutoField', so I'm puzzled as to why Django is making this migration.
The reason default_auto_field will likely not work is because it is defined on the model, not the app config.
I tried adding my own registration/apps.py into source code control with default_auto_field as AutoField, and that didn't work.
This will work… on your registration app, so the models defined in that app, not in other apps.
Likely the best way to fix this is to fix it on the package itself, by specifying an AppConfig that then defines, for the scope of that package, the default_auto_field. I submitted a pull request (#431) [GitHub] for this.

Related

Django model with historycal change

How can i show model historical changes and show them and can modify?
for example
class MM(models.Model):
chnge1=model.ForeignKey('self')
changeText = models.TextField(max_length=200)
If you are using Django to manage your models you can always look through the migration files to see what has changed. The migration files are designed to keep track of changes to your models so that Django can apply those changes to the database.
See more here: Django Migrations
A typical migration file might look like this:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.DeleteModel('Tribble'),
migrations.AddField('Author', 'rating', models.IntegerField(default=0)),
]
You can see the 'Tribble' model was delete and a 'rating' field was added to the 'Author' model.
If you have many migrations for a single app and don't want to go through each migration file to see the changes try "squashing" your migration files into (usually) a single file and you can go through all the changes from there.
Squashing Migrations: Squashing Migrations

Conditional Django migration based on a field only present in new version

My app that currently depends on Postgres and Django's Postgres-only JSONField. The field works well and I've no interest in another project, but I have prospective-users who want to use my app, but can't while it relies on Postgres.
Django 3.1 has a cross-platform version of this field —which will work for my needs— but I don't want to force everybody up to Django 3.1; I would like to offer people a choice between Postgres or Django 3.1+. On paper, this is simple enough with a conditional import...
try:
from django.db.models import JSONField
except ImportError:
from django.contrib.postgres.fields import JSONField
And if I installed Django 3.1 and generated a migration, it could take me from django.contrib.postgres.fields.JSONField to django.db.models.JSONField. But...
New users will still execute the initial migration. I will still have a dependency on Postgres.
Sub-Django 3.1 users won't be able to execute the new migration. I now have a dependency on Django 3.1.
This is worse than when I started. How do I do this sort of field migration in a way that will work for everybody?
Migrations are just code. Just because they're auto-generated doesn't mean you shouldn't change them. You're encouraged to, at least to check they're generated correctly, but also there's no harm in writing them yourself.
This works for me:
Model:
from django.db import models
try:
from django.db.models import JSONField
except ImportError:
from django.contrib.postgres.fields import JSONField
class MyModel(models.Model):
stuff = JSONField()
Migration:
from django.db import migrations, models
try:
from django.db.models import JSONField
except ImportError:
from django.contrib.postgres.fields import JSONField
class Migration(migrations.Migration):
dependencies = [('testapp', '0001_initial')]
operations = [
migrations.CreateModel(
name='MyModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stuff', JSONField()),
],
),
]
Keep in mind that if you need to change this field in the future, you will need to go through this process again.
I have got this from Django source code
from django.db.models import JSONField as BuiltinJSONField
class JSONField(BuiltinJSONField):
system_check_deprecated_details = {
'msg': (
'django.contrib.postgres.fields.JSONField is deprecated. Support '
'for it (except in historical migrations) will be removed in '
'Django 4.0.'
),
'hint': 'Use django.db.models.JSONField instead.',
'id': 'fields.W904',
}
This indicates that, django.contrib.postgres.fields.JSONField is going to be deprecated. Also, Django uses the django.db.models.JSONField as postgres special JSONField.
Apart from that, I have generated the SQL command using sqlmigrate command and it was like,
BEGIN;
--
-- Create model MyModel
--
CREATE TABLE "myapp_mymodel" ("id" serial NOT NULL PRIMARY KEY, "stuff" jsonb NOT NULL);
COMMIT;
Surprisingly, I have got same SQL command using Django==3.0 and Django==3.1 and in the database, the field is a jsonb type
These pieces of information conclude that you don't have to worry about this new JSONField upgrade.
What changes need to be done?
You don't need to generate a new migration file, but edit existing migration files which have django.contrib.postgres.fields.JSONField reference with the try...except block.
That's it!!!
All new migrations will be correct automatically if this solution with a deconstruct() method will be used.
You can create a compatible custom JSONField that encapsulates both variants.
Create a small file fields.py in your application:
try:
from django.db.models import JSONField as OrigJSONField
except ImportError:
from django.contrib.postgres.fields import JSONField as OrigJSONField
class JSONField(OrigJSONField):
def deconstruct(self):
# the original path was 'django.db.models.JSONField' or 'django.contrib.postgres.fields....'
name, path, args, kwargs = super().deconstruct()
# Substitute 'my_app' by your application name everywhere.
path = 'my_app.fields.JSONField'
return name, path, args, kwargs
Change a line in your models.py:
from my_app.fields import JSONField
Edit all existing migrations that use a JSONField:
import my_app.fields
...
('stuff', my_app.fields.JSONField()),
  (or equivalently)
from my_app.fields import JSONField
...
('stuff', JSONField()).
No migration is created after that because no difference is found by makemigrations.
All future makemigrations after a changed model will be created automatically with my_app.fields.JSONField(). Compatibility is a benefit of this solution.
Reflection about Django Release Notes 3.1 and future:
They describe a plain upgrade that requires to create a new migration that only upgrades the import path, but it generates no SQL e.g. by sqlmigrate. It is easier than to edit old migrations.
Maybe you you also upgrade in two years to Django >= 3.1 only and you will have two alternatives:
A) Only the import in models.py will be edited and you create a new nearly empty formal migration similarly to release notes. You will be not able to remove the import path my_app.fields.JSONField because it must be importable from old migrations similarly that Django can never remove the path django.contrib.postgres.fields.JSONField. The file my_app/fields.py could be simplified to one line from django.db.models import JSONField as JSONField after an unconditional upgrade of requirements.
B) Editing old migrations again to only the new JSONField is possible, but unreasonable.
(It is every user's responsibility that an edit in a migrations file doesn't require a changed database state and all migrations remain consistent each other. That is why not much about it can be found, except such clear cases.)
I gave point to Tom Carrick for his answer but I think it should also include the usage of Meta.required_db_vendor = 'postgres' on Django < 3.1
# models.py
from django.db import models
try:
from django.db.models import JSONField
postgres_only = False
except ImportError:
from django.contrib.postgres.fields import JSONField
postgres_only = True
class MyModel(models.Model):
stuff = JSONField()
class Meta:
if postgres_only:
required_db_vendor = 'postgres'
# migrations/0001_initial.py
from django.db import migrations, models
try:
from django.db.models import JSONField
postgres_only = False
except ImportError:
from django.contrib.postgres.fields import JSONField
postgres_only = True
class Migration(migrations.Migration):
dependencies = [('testapp', '0001_initial')]
operations = [
migrations.CreateModel(
name='MyModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stuff', JSONField()),
],
options={'required_db_vendor': 'postgres'} if postgres_only else None,
),
]
This will ensure that users of your reusable app cannot use it on a different database than PostgreSQL on Django < 3.1 and seamlessly enable support on 3.1 without requiring a migration on PostgreSQL users using your library and updating to Django 3.1.

Django makemigrations make change for 'auth.user' every time

I upgrade my system's django version from 1.6.10 to 1.8.16 for test.
On before version, i use South for migration.
So, I followed 'https://docs.djangoproject.com/en/1.7/topics/migrations/#upgrading-from-south' this documentation.
My problem is every makemigrations are check same field, then make migration file.
That field is 'auth.User' foreign key field. like user = models.ForeignKey('auth.User').
here are my screenshot for that problem.
This is Sample code for that foreign key field.
cancelled_by = models.ForeignKey(
'auth.User',
verbose_name=_("Cancelled by"),
related_name='project_cancel',
blank=True,
null=True
)
How can i fix it?
edited:
This is my migration file created by makemigrations after all migration.
class Migration(migrations.Migration):
dependencies = [
('meeting', '0003_meeting_proposal'),
]
operations = [
migrations.AlterField(
model_name='meeting',
name='manager',
field=models.ForeignKey(verbose_name='Manager', blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
]
I would first delete the recently made migration files and try makemigration again.
deleting migration files is a common solution since Django is smart enough to recreate them easily regardless of what has been changed in your folder.
you can fake these migrations too, but i prefer to keep my migration folder clean and tidy.

Using django-custom-user in existing django-cms project

I have an existing django-cms ver. 3.1.3 project where I would like to replace the default django User model with the one found in django-custom-user (to have email as user name for my users). I have added the custom_user app to my INSTALLED_APPS and set AUTH_USER_MODEL = 'custom_user.EmailUser'. Finally I applied the migrations.
Everything seems to work fine in my custom models, but the django-cms models that have a reference to the auth_user table (GlobalPagePermission, PagePermission, PageUser and UserSettings) are not updated to have a foreign-key reference to the new custom user table.
The django-cms documentation says that is generally advisable to add custom user models in the beginning of a project, but here I am, in the middle of a project, and would very much like to avoid having to delete my cms models (with data) to make it work.
If I were in the beginning of a project, and had added the custom user model before migrating the django-cms models, would the django-cms models actually get a reference to the custom user model table instead of the default one?
Is there any way for me to make migrations for the django-cms models, so they use the new custom user model instead of the default one?
UPDATE
Im trying to implement what yakky suggested:
from __future__ import unicode_literals
from django.conf import settings
from django.db import models, migrations
import django.db.models.deletion
from django.utils.translation import ugettext_lazy as _
def up(apps, schema_editor):
PageUser = apps.get_model("cms", "PageUser")
db_alias = schema_editor.connection.alias
for pu in PageUser.objects.all():
pu['emailuser_ptr_id'] = pu['user_ptr_id']
pu.save()
def down(apps, schema_editor):
PageUser = apps.get_model("cms", "PageUser")
db_alias = schema_editor.connection.alias
for pu in PageUser.objects.all():
pu['user_ptr_id'] = pu['emailuser_ptr_id']
pu.save()
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
(‘myapp’, ‘previous-migration’),
]
operations = [
migrations.RenameField(
model_name='PageUser',
old_name='user_ptr_id',
new_name='user_ptr_id_old',
),
migrations.AddField(
model_name='PageUser',
name='emailuser_ptr_id',
field=models.ForeignKey(null=True, to=settings.AUTH_USER_MODEL, verbose_name=_('user'), blank=True),
preserve_default=True,
),
migrations.RunPython(up, down),
migrations.RemoveField(
model_name='PageUser',
name='user_ptr_id_old',
),
]
It fails with KeyError: ('myapp', u'pageuser') suggesting that it looks for the PageUser model in the my custom app and not in the cms app. How do I apply these migrations to the cms models?
Being said that replacing the Django User model it's not advisable in the middle of a project lifetime (see https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#substituting-a-custom-user-model) as Django does not provide any way to modify the relations after their are being created, to replace the custom user model you can write a custom migration that alter the foreignkey in the django CMS models to point to theand create a new one to the new user model.
Something like:
...
migrations.RenameField(
model_name='globalpagepermission',
old_name='user',
new_name='user_old',
),
migrations.AddField(
model_name='globalpagepermission',
name='user',
field=models.ForeignKey(null=True, to=settings.AUTH_USER_MODEL, verbose_name=_('user'), blank=True),
preserve_default=True,
),
migrations.RunPython(copy_data),
migrations.RemoveField(
model_name='globalpagepermission',
name='user_old',
),
...
Repeat for any relevant model / field.
copy_data method will copy the value for the foreign keys from user_old to user
Code is completely untested but the general idea should work.
Alternatively you can write a RunSQL migration to get the same result.
Users with the same primary keys must exist also for the new model for this to work.
Thanks to https://code.djangoproject.com/ticket/25313 I managed to make it work. This is what I did:
Backed up my database!
Created a new app email_username to hold my user model (named User as recommended in the link). A new app was necessary because my main app (myapp) is dependent on django-cms, and if I put my new user model in myapp, django-cms would become dependent on my app - this would create a circular reference.
class User(AbstractEmailUser):
# Needed to add username, first_name and last_name here, because cms.PageUser model depends on these fields
username = models.CharField(max_length=100, verbose_name=_('username'))
first_name = models.CharField(max_length=100, verbose_name=_('first_name'))
last_name = models.CharField(max_length=100, verbose_name=_('last_name'))
class Meta:
db_table = 'auth_user' # the model will use the existing auth_user table (at first)
Set AUTH_USER_MODEL = ‘email_username.User’ in settings.py
Deleted all migrations of myapp and truncated the django_migrations table
Ran manage.py makemigrations and got
Cannot resolve bases for [] This can
happen if you are inheriting models from an app with migrations (e.g.
contrib.auth) in an app with no migrations; see
https://docs.djangoproject.com/en/1.8/topics/migrations/#dependencies
for more
... so I removed the cms app from settings.py, ran makemigrations again which now suceeded, then re-added and ran makemigrations again.
Ran manage.py migrate --fake, but got
Cannot resolve bases for [ModelState: 'djangocms_file.File', ModelState: 'djangocms_video.Video', ModelState: 'djangocms_link.Link', ModelState: 'djangocms_googlemap.GoogleMap', ModelState: 'djangocms_picture.Picture', ModelState: 'djangocms_teaser.Teaser']
... so I removed these apps as well from settings.py, ran migrate --fake, re-added the apps and migrate --fake again.
Removed the db_table setting from the new User model, ran makemigrations followed by migrate (without fake!)
Everything now seems to be in order. All foreign keys that previously referenced the standard auth_user table is now referincing the table of my new user model. Existing users have even been transferred because of the db_table trick.

Do something once in django

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.)