CarstenI have made some progress since documenting it and intend to refine it 
more.Experimenting has boosted my confidence in Postgres and there is a bit of 
scripting involved to fake-apply migrations in the correct sequence.At the 
moment I'm time poor and docco has to wait but can say I got both methods 
working. I decided in the end to go with Aymeric's because it is simpler and 
importantly for me resulted in reducing the gross number of migration files - 
which of course get frequently scanned by Django during unit testing. So that 
is a continuing bonus.More later ... but (ymmv) I am confirming :)CheersMike 
-------- Original message --------From: Carsten Fuchs <carsten.fu...@cafu.de> 
Date: 26/12/19  20:01  (GMT+10:00) To: django-users@googlegroups.com Subject: 
Re: Changing to a custom user model mid-project Hello Mike,unfortunately I 
cannot answer your questions, but have you considered contacting Tobias 
directly?I too am currently at the point of switching two of my projects to a 
custom user model. But honestly, Tobias' procedure looks complicated and error 
prone to me. Aymeric's description doesn't cover some of the details (some of 
your and Tobias' steps are needed there too), but at least for me it is easier 
to understand and therefore I feel more comfortable with it.I was wondering 
though if I properly understand the downside of Aymeric's approach: All that it 
means is that once it is done, we cannot checkout an earlier version of the 
code across the breaking step and un-apply the migrations that were added since 
then?Except in trivial circumstances, I've never done this anyway, and 
considering the lower complexity, it seems a small price to pay.Can anyone 
confirm this?Best regards,CarstenAm 20.12.19 um 05:12 schrieb Mike Dewhirst:> 
I'm documenting[*] the process I followed but have come to a gray area and need 
some expert assistance.> > Having achieved a working system with a new database 
table containing all the existing data and now called ...> >     
public.common_user> > ... I notice that it has a user_id sequence called ...> > 
    public.auth_user_id_seq> > ... along with similarly named sequences for 
similarly named tables which now exist as common_...  tables.> > I can easily 
enough rename those sequences so they match the owning tables - and I want to 
do so - but the question is should it be done via raw SQL within the migration 
system?> > Is there a proper way to align the names?> > Another way (which I've 
tested) is to edit a database dump and reload that.> > What is the correct 
approach? Is it even legal (ORM rules) to rename the table?> > Thanks for any 
advice> > Mike> > > > > [*] 
https://www.caktusgroup.com/blog/2019/04/26/how-switch-custom-django-user-model-mid-project/
 by Tobias McNulty as a variation of Django docs 
https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#changing-to-a-custom-user-model-mid-project>
 > TL;DR> > Custom user documentation (UNFINISHED DRAFT)> > Based on Tobias 
McNulty's "How to Switch to a Custom Django User Model Mid-Project"[1]> > 
Assumptions> - Existing project without a custom user model> - All migrations 
are up to date and deployed in production> - Existing auth_user table has data 
which must be kept> - Relationships with other models exist and must be kept> > 
Strategy> > There are two strategies. One is to throw away history, delete all 
migrations, empty (truncate) the migrations table and start again.[2] Very 
attractive if the project repo is young and history is fresh and therefore 
disposable.> > The other strategy is to use the migration system to make the 
switch, ensuring nothing breaks. That is the Tobias approach and the one 
documented here.> > This strategy is a genuine bottleneck. All pending changes 
must be completed and fully deployed before starting and no planned changes are 
commenced until after the switch is fully deployed.> > Objective> > - 
Completely align development, staging and production systems> - Series of new 
migrations> - Series of sql commands to adjust content_type records> - Series 
of scripts to execute migrations and sql commands> > Process> > 1. Ensure all 
references to User everywhere (including 3rd party apps) are indirect[3][4]. 
Ensure all code concerned with access control and relying on users or user 
authentication is covered by unit tests as far as possible and all tests are 
passing.> > > 2. Make migrations and apply them. Ensure development, staging 
and production systems are all synchronised and each database (structure) is 
identical. This starts the bottleneck.> > > 3. Start a new app or use an 
existing one which has no models.py. The reason there needs to be initially no 
models is the migration which creates the custom user must be '0001_initial.py' 
to persuade Django there are no dependency issues. In this documentation I call 
the app "common" but it can be anything eg "proj_user", "accounts" etc.> >     
python manage.py startapp common> > > 4. Write a new common/models.py ...> >    
 from django.db import models>     from django.contrib.auth.models import 
AbstractUser> > >     class User(AbstractUser):>         """ Retain the model 
name 'User' to avoid unnecessary refactoring during>         the switchover 
process. Make no other changes here until after complete>         deployment to 
production.>         """>         class Meta:>             # use the existing 
Django users table for the initial migration>             db_table = 
"auth_user"> > > 5. Write a new common/admin.py> >     from django.contrib 
import admin>     from django.contrib.auth.admin import UserAdmin>     from 
.models import User> > >     admin.site.register(User, UserAdmin)> > > 6. 
Include the new app in settings.py among other local apps and adjust 
AUTH_USER_MODEL ...> >     INSTALLED_APPS = [>         # ...>         
'common',>     ]> >     AUTH_USER_MODEL = 'common.User'> > > 7. Make the 
initial migration to create the new User model ...> >     python manage.py 
makemigrations  --> common/migrations/0001_initial.py> > > 8. Write a script to 
deploy (rather than execute) the migration as follows ... [5]> > Windows 10 - 
PostgreSQL 10 ...> >     :: deploy_migration.bat>     :: defeat Django's sanity 
check by manually entering that migration in the database>     :: and for good 
measure update content_types to avoid further Django sanity checks> >     set 
host=dev_laptop>     set dbowner=whoever> >     psql --username=%dbowner% 
--port=5432 --dbname=ssds --host=%host% --command "INSERT INTO 
public.django_migrations (app, name, applied) VALUES ('common', '0001_initial', 
CURRENT_TIMESTAMP)";> >     psql --username=%dbowner% --port=5432 --dbname=ssds 
--host=%host% --command "UPDATE public.django_content_type SET app_label = 
'common' WHERE app_label = 'auth' and model = 'user'";> > > Linux (Ubuntu 
18.04) - PostgreSQL 10 ...> >     # fetch_ssds.py [6]>     # These next two 
psql command lines fake an initial migration to create>     # a custom-user in 
a pre-existing project and adjust content_types to>     # prevent Django from 
barfing if it automatically tried to add them>     #>     import os> >     
host="dev_laptop">     dbowner="whoever"> >     cmd = "sudo psql --username=%s 
--port=5432 --dbname=ssds --host=%s --command \"INSERT INTO 
public.django_migrations (app, name, applied) VALUES ('common', '0001_initial', 
CURRENT_TIMESTAMP);\"" % (dbowner, host)>     #>     os.system(cmd)>     #>     
cmd = "sudo psql --username=%s --port=5432 --dbname=ssds --host=%s --command 
\"UPDATE public.django_content_type SET app_label = 'common' WHERE app_label = 
'auth' and model = 'user';\"" % (dbowner, host)>     #>     os.system(cmd)> > > 
9. After deploying with the above technique in development run all unit tests 
and correct any errors or failures both in project code and in the above 
scripts. Refresh the development database (structure) from production (again) 
and repeat step 8 above and test again. All unit tests must pass. Important - 
repeat until perfect.> > > 10. Deploy to staging using one of the above scripts 
from step 8, modified for the staging environment. When perfectly deployed on 
staging and all testing is done, ensure production is backed up then deploy to 
production in similar fashion. This ends the bottleneck.> > The balance of this 
process is optional> > > 11. To be written after resolving sequence naming 
questions> > > 12. Edit common/models.py then makemigrations to rename the 
table of existing users from auth_user to common_user. Finally migrate to 
execute the rename to common_user> >     class User(AbstractUser):>         """ 
Retain the model name 'User' to avoid unnecessary refactoring during>         
the switchover process. Make no other changes here until after complete>        
 deployment to production.> >         Comment out Meta entirely to migrate to 
the default table name>         """>         pass> >         #class Meta:>      
   #    # use the existing Django users table for the initial migration>        
 #    db_table = "auth_user"> > > > [1] 
https://www.caktusgroup.com/blog/2019/04/26/how-switch-custom-django-user-model-mid-project/
 by Tobias McNulty as a variation of Django docs 
https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#changing-to-a-custom-user-model-mid-project>
 > [2] https://code.djangoproject.com/ticket/25313#comment:2 by Aymeric 
Augustin> > [3] 
https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#referencing-the-user-model>
 > [4] Note that get_user_model() cannot be called at the module level in any 
models.py file (and by extension any file that a models.py imports), since 
you'll end up with a circular import. Generally, it's easier to keep calls to 
get_user_model() inside a method whenever possible (so it's called at run time 
rather than load time), and use settings.AUTH_USER_MODEL in all other cases. 
This isn't always possible (e.g., when creating a ModelForm), but the less you 
use it at the module level, the fewer circular imports you'll have to stumble 
your way through. (From Tobias [1])> > [5] Tobias notes that Django won't 
permit 'migrate common --fake-initial' if there are other migrations which 
include settings.AUTH_USER_MODEL> > [6] fetch_ssds.py is a comprehensive 
auto-deployment script. Only the relevant (and simplified) portion is shown.> > 
-- You received this message because you are subscribed to the Google Groups 
"Django users" group.To unsubscribe from this group and stop receiving emails 
from it, send an email to django-users+unsubscr...@googlegroups.com.to view 
this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/0eb107a7-cefe-fa3f-e145-088225c084d9%40cafu.de.

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/5e04a086.1c69fb81.484db.03b0SMTPIN_ADDED_MISSING%40gmr-mx.google.com.

Reply via email to