I have an old Django Project, which I started when I was a beginner. So far it worked but, due to some code refactoring I would like to do, I would like to change the original database models. Basically, I originally made many different models, each one for a user type.
old models:
class CustomUser(AbstractUser):
user_type_data = (
('admin', 'Admin'),
('instructor', 'Instructor'),
('student', 'Student'),
('renter', 'Renter'),
)
user_type = models.CharField(
max_length=20, choices=user_type_data, default=1)
class Admin(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
date_of_birth = models.DateField(null=True, blank=True)
fiscal_code = models.CharField(max_length=50, null=True, blank=True)
phone = models.CharField(max_length=50, null=True, blank=True)
picture = models.ImageField(
blank=True, null=True, default='default.png')
address = models.CharField(max_length=100, blank=True, null=True)
cap = models.CharField(max_length=10, blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
province = models.CharField(
max_length=100, choices=PROVINCE_CHOICES, blank=True, null=True)
country = CountryField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.user.username
class Meta:
ordering = ['last_name']
class Instructor(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
date_of_birth = models.DateField(null=True, blank=True)
fiscal_code = models.CharField(max_length=50, null=True, blank=True)
phone = models.CharField(max_length=50, null=True, blank=True)
picture = models.ImageField(
blank=True, null=True, default='default.png')
address = models.CharField(max_length=100, null=True, blank=True)
cap = models.CharField(max_length=10, null=True, blank=True)
city = models.CharField(max_length=100, null=True, blank=True)
province = models.CharField(
max_length=100, choices=PROVINCE_CHOICES, blank=True, null=True)
country = CountryField(blank=True, null=True)
is_active = models.BooleanField(default=True)
flight_wage = models.FloatField(null=True, blank=True)
theory_wage = models.FloatField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.last_name ' ' self.user.first_name
class Meta:
ordering = ['last_name']
I posted just the first two user type but you get the picture. A lot of redundant code. What I would like to achieve is something like that:
new models.py:
class CustomUser(AbstractUser):
user_type_data = (
('admin', 'Admin'),
('instructor', 'Instructor'),
('student', 'Student'),
('renter', 'Renter'),
)
user_type = models.CharField(
max_length=20, choices=user_type_data, default=1)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
date_of_birth = models.DateField(null=True, blank=True)
fiscal_code = models.CharField(max_length=50, null=True, blank=True)
phone = models.CharField(max_length=50, null=True, blank=True)
picture = models.ImageField(
blank=True, null=True, default='default.png')
address = models.CharField(max_length=100, null=True, blank=True)
cap = models.CharField(max_length=10, null=True, blank=True)
city = models.CharField(max_length=100, null=True, blank=True)
province = models.CharField(
max_length=100, choices=PROVINCE_CHOICES, blank=True, null=True)
country = CountryField(blank=True, null=True)
is_active = models.BooleanField(default=True)
flight_wage = models.FloatField(null=True, blank=True)
theory_wage = models.FloatField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.last_name ' ' self.user.first_name
class Meta:
ordering = ['last_name']
My question is: Is it possibile to adapt my old projects (I already have two customers using the old project) to this new brand type of database model?
CodePudding user response:
I would do this with the following steps:
- Add new fields to existing user model, make sure to add null=True for all new fields
- Create migrations file for the user model
- Inside the migrations file add a migrations.RunPython command to add the values from the Admin model to each corresponding user.
- Run Migration and remove null=True from all fields where not necessary
- Refactor all code to refer to user model instead of admin model
- Remove admin model
The same can be done for the Instructor model
I'll give an example for step 3 here:
## Example migration file
from django.db import migrations
def add_values_to_user(apps, schema):
CustomUser = apps.get_model('users', 'CustomUser')
for user in CustomUser.objects.all():
if user.admin:
user.first_name = user.admin.first_name
elif user.instructor:
user.first_name = user.instructor.first_name
# Do this for all values you require for the admin and instructor model
user.save()
class Migration(migrations.Migration):
dependencies = [
('users', '0046_auto_20220506_1523'),
]
operations = [
migrations.RunPython(add_values_to_user)
]
CodePudding user response:
Maybe what you are looking for are Proxy models: https://docs.djangoproject.com/en/4.1/topics/db/models/#proxy-models
From docs: This is what proxy model inheritance is for: creating a proxy for the original model. You can create, delete and update instances of the proxy model and all the data will be saved as if you were using the original (non-proxied) model. The difference is that you can change things like the default model ordering or the default manager in the proxy, without having to alter the original.
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
The MyPerson class operates on the same database table as its parent Person class. In particular, any new instances of Person will also be accessible through MyPerson, and vice-versa