Запуск миграций для нового добавленного поля, которое переопределяет сохранение - PullRequest
0 голосов
/ 11 марта 2019

Я работал с программным обеспечением для управления клиникой, где до сих пор регистрировал пациента, вводя его данные, включая возраст в годах и месяцах.Это было сохранено в двух полях Integer.Однако я понял, что в следующем году, если тот же пациент посетит, его возраст все равно будет показан как тот же.Следовательно, мне нужно было создать приблизительную дату рождения (я не настаиваю на том, чтобы пациенты указывали дату своего рождения при регистрации.)

Поэтому я реализовал функцию, которая вычисляла бы приблизительную дату рождения по текущему возрасту.Я переопределил метод сохранения, чтобы в случае, если пациент не вводил дату рождения, в базу данных была вставлена ​​указанная дата.

Моя проблема заключается в создании этого для существующих клиентов.Как только я обновил свою модель с новым DateField и методом save, при попытке запустить makemigrations, мне предложили ввести значение по умолчанию, и я не знаю, как заставить makemigrations использовать мою функцию для обновления записей существующихпациентов.Должен ли я просто установить поле пустым и null true, а затем запустить пользовательский метод, чтобы проверить, является ли поле пустым, и обновить данные поля?Или я могу заставить makemigrations запустить функцию, которую я реализовал?Как правильно это сделать?

Моя модель и метод:

class customer(models.Model):
    # Need autoincrement, unique and primary
    cstid = models.AutoField(primary_key=True, unique=True)
    name = models.CharField(max_length=35)
    ageyrs=models.IntegerField(blank=True)
    agemnths=models.IntegerField(blank=True)
    dob = models.DateField()
    gender_choices = (
        ('male', 'Male'),
        ('female', 'Female'),
        ('other', 'Something else'),
        ('decline', 'Decline to answer')
        )
    gender = models.CharField(
        choices=gender_choices, max_length=10, default='male')
    maritalstatus_choices = (
        ('unmarried', 'Unmarried'),
        ('married', 'Married')
                            )
    maritalstatus = models.CharField(
        choices=maritalstatus_choices, max_length=10, default='Unmarried')
    mobile = models.CharField(max_length=15, default='')
    alternate = models.CharField(max_length=15, default='', blank=True)
    email = models.CharField(max_length=50, default='', blank=True)
    address = models.CharField(max_length=80, default='', blank=True)
    city = models.CharField(max_length=25, default='', blank=True)
    occupation = models.CharField(max_length=25, default='', blank=True)
    bloodgroup_choices = (('apos', 'A+'),
        ('aneg', 'A-'),
        ('bpos', 'B+'),
        ('bneg', 'B-'),
        ('opos', 'O+'),
        ('oneg', 'O-'),
        ('abpos', 'AB+'),
        ('abneg', 'AB-')
        )
    bloodgroup = models.CharField(choices=bloodgroup_choices, max_length=5, default='-', blank=True)
    linkedclinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)

    class Meta:
        unique_together = ["name", "mobile", "linkedclinic"]


    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.dob:
            current = datetime.datetime.now()
            newdate = current - relativedelta(years=self.ageyrs, months=self.agemnths)
            if not self.ageyrs == 0:
    #           datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
                dob = datetime.datetime(newdate.year, 1, 1)
            else:
                print("Age is less than 1 year")
                dob = newdate
            self.dob = dob
        super().save(*args, **kwargs)  # Call the "real" save() method.

    def age(self):
        if self.ageyrs == 0 and self.agemnths == 0:
            return "0yr"
        if self.ageyrs == 0:
            return str(self.agemnths) + "m"
        if self.agemnths == 0:
            return str(self.ageyrs) +"yr"
        return str(self.ageyrs) +"yr " +  str(self.agemnths) + "m"

При выполнении makemigrations:

joel@hp:~/myappointments$ ./manage.py makemigrations
You are trying to add a non-nullable field 'dob' to customer without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 

1 Ответ

2 голосов
/ 11 марта 2019

Обычно я имею дело с такими изменениями модели, используя три миграции:

  1. Добавить новое поле с null=True
  2. Заполнить пустые поля
  3. Изменить только что добавленное поле на null=False

Вторая миграция может использовать RunSQL или RunPython и, например, выглядеть так:

def forward(apps, schema_editor):
    customer = apps.get_model("<app_name>", "customer")
    db_alias = schema_editor.connection.alias
    current = datetime.datetime.now()
    for c in customer.objects.using(db_alias).all():
        newdate = current - relativedelta(years=c.ageyrs, months=c.agemnths)
        if not self.ageyrs == 0:
            dob = datetime.datetime(newdate.year, 1, 1)
        else:
            print("Age is less than 1 year")
        dob = newdate
        c.dob = dob.date()
        c.save()


def rollback(apps, schema_editor):
    pass


...
    operations = [migrations.RunPython(forward, rollback)]

Вы можете сгенерировать его, используя python manage.py makemigrations <app_name> --empty, а затем изменить его, указав правильные данные. Когда вы будете создавать третью миграцию, в размещенной вами подсказке будет третий вариант, например «Я имел дело с этим вручную, используя в предыдущей миграции».

...