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.
Related
I have added a UUID to the following model:
class Post(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
...
But there already are entries in the database that were created without the uuid field.
When I run migrate, it adds the same UUID to all my previous objects.
Is there an easy way of populating the existing objects with a different UUID automatically?
I think the easiest way to fix this is to make a data migration [Django-doc]. You create a new migration file with:
python manage.py makemigrations --empty yourappname
This will create a new file in the migrations/ directory that does nothing. We can alter that migration file with something that looks like:
# Generated by Django 3.2 on 2021-05-01 12:49
from django.db import migrations
from uuid import uuid4
class Migration(migrations.Migration):
def populate_uuid(apps, schema_editor):
Post = apps.get_model('yourappname', 'Post')
posts = list(Post.objects.all())
for post in posts:
post.uuid = uuid4()
Post.objects.bulk_update(posts, ['uuid'])
dependencies = [
('yourappname', 'previous_migrationname'),
]
operations = [
migrations.RunPython(populate_uuid)
]
If you then migrate, it will load all Post objects, provide these each a unique uuid, and then update the items in bulk.
How can I tell DSH to index particular field? Some queries that I do to historical models take too much time
I've base abstract model and all my models inherit from that model. The history field is also defined in this base model:
class BaseModel(models.Model):
class PublishingStatus(models.TextChoices):
DRAFT = 'draft', _('Draft')
ACCEPTED = 'accepted', _('Accepted'),
REJECTED = 'rejected', _('Rejected'),
MODIFIED = 'modified', _('Modified')
publishing_status = models.CharField(
max_length=9,
choices=PublishingStatus.choices,
default=PublishingStatus.DRAFT,
help_text=_("Publishing status represents the state of the object. By default it is 'draft'")
)
history = HistoricalRecords(inherit=True)
And I've also added indexes in this base model
class Meta:
abstract = True
indexes = [models.Index(fields=[
'publishing_status',
])]
It would be nice if Django Simple History could check which fields are indexed and create the same indexes in the historical models
Maybe there is a way to tell django simple history explicitly which field must be indexed additionally?
I hope there is a better solution, but I've ended up creating a custom migration file. First, I created a blank migration file for the app:
python manage.py makemigrations dosh --empty
I then modified this newly created empty migration file and added AddIndex for each historical model.
# Generated by Django 3.1.1 on 2020-10-15 06:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dosh', '0002_auto_20201008_2155'),
]
operations = [
# ...
migrations.AddIndex(
model_name='historicaltense',
index=models.Index(fields=['publishing_status'], name='htense_publish_status_idx')
),
migrations.AddIndex(
model_name='historicalsynonym',
index=models.Index(fields=['publishing_status'], name='hsynonym_publish_status_idx')
),
# ...
]
And finally, I've migrated those changes
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, authtoken, comment, contenttypes, dosh, pinax_badges, sessions, social, users, vote
Running migrations:
Applying dosh.0003_auto_20201015_0849... OK
I'm sure this has been asked before, but I cannot find the answer. In django, if I have this model
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
How would one populate this model with a default Person entry when the first migrating the field?
I'm not talking about default values for the fields, but for default entries in the database.
thanks
You can make a data migration, that follows on the migration where you create the Person object. You can first let Django write the "skeleton" of the migration, this can be done with:
python manage.py makemigrations --empty appname
Next Django will make a file. In that file you can add RunPython item to the operations list. This then obtain the historical model (the model at that moment of the migration), where you then create a Person object in the database. For example with:
from django.db import migrations
class Migration(migrations.Migration):
def create_person(apps, schema_editor):
Person = apps.get_model('appname', 'Person')
Person.objects.create(first_name='will', last_name='mendil')
dependencies = [
('appname', 'migrationname'),
]
operations = [
migrations.RunPython(create_person)
]
(Django 1.10.) I'm trying to follow this advice on extending the user model using OneToOneField. In my app 'polls' (yes, I'm extending the app made in the 'official' tutorial) I want to store two additional pieces of information about each user, namely, a string of characters and a number.
In my models.py I now have the following:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
stopien = models.CharField(max_length=100)
pensum = models.IntegerField()
and in admin.py the following:
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from polls.models import Employee
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = 'employee'
class UserAdmin(BaseUserAdmin):
inlines = (EmployeeInline, )
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
When adding a user using the admin panel my two new fields display correctly. However, when I click 'save', or if I don't add any user and just click on the name of my sole admin user in the admin panel, I get the following error:
OperationalError at /admin/auth/user/1/change/
no such table: polls_employee
I see some questions and answers related to similar problems, but they seem to be relevant for older version of Django. Could anyone give me a tip as to what I should do? Ideally I'd want my two additional fields display in the admin panel, though I suspect this might be a task for the future.
I have to confess I do not understand this paragraph from the documentation just following the advice I'm using:
These profile models are not special in any way - they are just Django models that happen to have a one-to-one link with a User model. As such, they do not get auto created when a user is created, but a django.db.models.signals.post_save could be used to create or update related models as appropriate.
Do I need to tie this 'post-save' to some element of the admin panel?
I'd be very greatful for any help!
You need run makemigrations to create a migration for your new model, and then migrate to run the migration and create the database table.
./manage.py makemigrations
./manage.py migrate
After customizing my user model in Django Oscar, I received the following error message:
IntegrityError at /
insert or update on table "basket_basket" violates foreign key constraint "basket_basket_owner_id_74ddb970811da304_fk_auth_user_id"
DETAIL: Key (owner_id)=(5) is not present in table "auth_user".
To customize my user model, I followed the instructions here.
First, I wrote the following models.py file, located within my project directory at apps/user/models.py.
from django.db import models
from oscar.apps.customer.abstract_models import AbstractUser
from django.contrib.postgres.fields import ArrayField
class User(AbstractUser):
acct_bal = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
purchased_items = ArrayField(models.IntegerField(), default=list)
The idea is that I want the user to have an account balance (which I will use for payment later) as well as a list of product numbers representing items that have already been purchased.
After making models.py, I edited the installed apps as follows:
INSTALLED_APPS = [...
'shopworld.apps.user',
] + get_core_apps()
And then put this at the bottom of my settings.py:
AUTH_USER_MODEL = 'user.User'
I then did ./manage.py migrate, but for some reason I am getting this error message. I also tried dropping the django_admin_log table as suggested here, but it did not work. Any help would be greatly appreciated.
I fixed this - the issue was that I was trying to migrate to a custom user model after already having done migrations with auth_user. This meant that auth_user didn't update correctly. I had to flush and re-sync the database, so that the initial migration captured the custom user model.