Reputation: 1767
Currently I have a legacy app which refers to a user
table with all the custom fields. Since there is a good amount of legacy code referring to that table I cannot simple rename that table as auth_user
. So the thing I'm trying to do is somehow merge (i don't know that its the right term) auth_user
and user
.
Below is user
table:
+-------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+----------------+
| user_id | int(10) | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| address | varchar(100) | NO | | NULL | |
| phone_no | varchar(15) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | varchar(100) | NO | | NULL | |
| pin_no | int(10) | NO | | NULL | |
| type | varchar(100) | NO | | NULL | |
| email | varchar(100) | NO | | NULL | |
| password | varchar(100) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| role | varchar(40) | NO | | NULL | |
| creation_date | int(100) | NO | | NULL | |
| edit_date | int(100) | NO | | NULL | |
| country | varchar(255) | NO | | NULL | |
| district | varchar(255) | NO | | NULL | |
| ip | varchar(255) | NO | | NULL | |
| added_by | int(11) | NO | | NULL | |
| is_phone_verified | binary(1) | NO | | 0 | |
| remember_token | varchar(100) | YES | | NULL | |
| disclaimer_agreed | int(11) | YES | | 0 | |
| mobile_login | tinyint(4) | NO | | 0 | |
+-------------------+--------------+------+-----+---------+----------------+
and django's auth_user
table:
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| password | varchar(128) | NO | | NULL | |
| last_login | datetime(6) | YES | | NULL | |
| is_superuser | tinyint(1) | NO | | NULL | |
| username | varchar(150) | NO | UNI | NULL | |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| email | varchar(254) | NO | | NULL | |
| is_staff | tinyint(1) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| date_joined | datetime(6) | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
What I would want is a single table, that django will refer to in using contrib.auth
related stuff and also at the same time does not deprecate my legacy code. maybe something like this:
+-------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+----------------+
| user_id | int(10) | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| address | varchar(100) | NO | | NULL | |
| phone_no | varchar(15) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | varchar(100) | NO | | NULL | |
| pin_no | int(10) | NO | | NULL | |
| type | varchar(100) | NO | | NULL | |
| email | varchar(100) | NO | | NULL | |
| password | varchar(100) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| role | varchar(40) | NO | | NULL | |
| creation_date | int(100) | NO | | NULL | |
| edit_date | int(100) | NO | | NULL | |
| country | varchar(255) | NO | | NULL | |
| district | varchar(255) | NO | | NULL | |
| ip | varchar(255) | NO | | NULL | |
| added_by | int(11) | NO | | NULL | |
| is_phone_verified | binary(1) | NO | | 0 | |
| remember_token | varchar(100) | YES | | NULL | |
| disclaimer_agreed | int(11) | YES | | 0 | |
| mobile_login | tinyint(4) | NO | | 0 | |
| last_login | datetime(6) | YES | | NULL | |
| is_superuser | tinyint(1) | NO | | NULL | |
| username | varchar(150) | NO | UNI | NULL | |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| is_staff | tinyint(1) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| date_joined | datetime(6) | NO | | NULL | |
+-------------------+--------------+------+-----+---------+----------------+
The motive here is to take advantage of Django's builtin
authentication
andpermission
system but without breaking legacy code. I think there must be a solution for this as this is not the first time someone is porting some legacy application to django.
I would also like to mention this link How to Extend Django User Model, but I'm not sure which approach is the best to adopt or should I be doing something completely different
Upvotes: 4
Views: 4372
Reputation: 15451
We can read in more detail from a Django ticket how to migrate from a built-in User model to a custom User model.
Quoting from Carsten Fuchs
Assumptions
- Your project doesn't have a custom user model yet.
- All existing users must be kept.
- There are no pending migrations and all existing migrations are applied.
- It is acceptable that all previous migrations are lost and can no longer be unapplied, even if you use version control to checkout old commits that still have the migration files. This is the relevant downside of this approach.
Preparations
- Make sure that any third party apps that refer to the Django User model only use the generic referencing methods.
- Make sure that your own reusable apps (apps that are intended to be used by others) use the generic reference methods.
- I suggest to not do the same with your project apps: The switch to a custom user model is only done once per project and never again. It is easier (and in my opinion also clearer) to change
from django.contrib.auth.models import User
to something else (as detailed below) than replacing it with generic references that are not needed in project code.- Make sure that you have a backup of your code and database!
Update the code
You can create the new user model in any existing app or a newly created one. My preference is to create a new app:
./manage.py startapp Accounts
I chose the name "Accounts", but any other name works as well.
Aymeric: „Create a custom user model identical to
auth.User
, call itUser
(so many-to-many tables keep the same name) and setdb_table='auth_user'
(so it uses the same table).“ InAccounts/models.py
:from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): class Meta: db_table = 'auth_user'
In
settings.py
, add the app toINSTALLED_APPS
and update theAUTH_USER_MODEL
setting:INSTALLED_APPS = ( # ... 'Accounts', ) AUTH_USER_MODEL = 'Accounts.User'
In your project code, replace all imports of the Django user model:
from django.contrib.auth.models import User with the new, custom one:
from Accounts.models import User
Delete all old migrations. (Beforehand, see if comment 14 is relevant to you!) For example, in the project root:
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete find . -path "*/migrations/*.pyc" -delete
Create new migrations from scratch:
./manage.py makemigrations
Make any changes to your admin.py files as required. (I cannot give any solid information here, but this is not crucial for the result and so the details can still be reviewed later.)
- Make sure that your testsuite completes successfully! (A fresh test database must be used, it cannot be kept from previous runs.)
- At this point, the changes to the code are complete. This is a good time for a commit.
Note that we're done – except that the new migration files mismatch the contents of the
django_migrations
table.(It may even be possible to serve your project at this point: It's easy to back off before the database is actually changed. Only do this if you understand that you cannot even touch the migrations system as long as the steps below are not completed!)
Update the database
Truncate the django_migrations table. MySQL 8 example:
TRUNCATE TABLE django_migrations;
This is possibly different for other databases or verions of MySQL < 8.
Fake-apply the new set of migrations ./manage.py migrate --fake
Check the ContentTypes as described at comment 12
Conclusion
- The upgrade to the custom user model is now complete. You can make changes to this model and generate and apply migrations for it as with any other models.
- As a first step, you may wish to unset
db_table
and generate and apply the resulting migrations.- In my opinion, the startproject management command should anticipate the introduction of a custom user model.
Upvotes: 0
Reputation: 599630
Django explicitly supports custom user models. Create a model for your existing table, make it inherit from AbstractBaseUser, and set the AUTH_USER_MODEL
setting to point to your new model. See the comprehensive docs.
Upvotes: 2