Как я могу объединить различные модели Db в одну?

У меня есть старый проект Django, который я начал, когда был новичком. До сих пор он работал, но из-за некоторого рефакторинга кода, который я хотел бы сделать, я хотел бы изменить исходные модели базы данных. В принципе, я изначально создал много различных моделей, каждая из которых предназначена для определенного типа пользователей.

старые модели:

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']

Я опубликовал только первые два пользовательских типа, но вы получите картину. Много лишнего кода. Я бы хотел добиться примерно следующего:

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']

Мой вопрос: возможно ли адаптировать мои старые проекты (у меня уже есть два клиента, использующих старый проект) к этому новому типу модели базы данных?

Возможно, то, что вы ищете - это Proxy модели: https://docs.djangoproject.com/en/4.1/topics/db/models/#proxy-models

Из документации: 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

Я бы сделал это с помощью следующих шагов:

  1. Добавьте новые поля в существующую модель пользователя, убедитесь, что добавили null=True для всех новых полей
  2. .
  3. Создайте файл миграций для модели пользователя
  4. Внутри файла migrations добавьте команду migrations.RunPython для добавления значений из модели Admin к каждому соответствующему пользователю.
  5. Запустите миграцию и удалите null=True из всех полей, где это не нужно
  6. .
  7. Переделайте весь код так, чтобы он ссылался на модель пользователя вместо модели администратора
  8. .
  9. Удалите модель администратора

То же самое можно сделать для модели инструктора

Здесь я приведу пример для шага 3:

## 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)
    ]



Вернуться на верх