What value do I use for _ptr when porting re-allocated classes using the South?

I have two classes, one of which comes from the other, and I would like to make them both classes of brothers belonging to the same base class.

Before:

from django.db import models class A(models.Model): name = models.CharField(max_length=10) class B(models.Model): title = models.CharField(max_length=10) 

After:

 from django.db import models class Base(models.Model): name = models.CharField(max_length=10) class A(Base): pass class B(Base): title = models.CharField(max_length=10) 

When I generate a schema migration, this is the result, including my answers to the questions:

 + Added model basetest.Base ? The field 'B.a_ptr' does not have a default specified, yet is NOT NULL. ? Since you are removing this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? 3. Disable the backwards migration by raising an exception. ? Please select a choice: 3 - Deleted field a_ptr on basetest.B ? The field 'B.base_ptr' does not have a default specified, yet is NOT NULL. ? Since you are adding this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? Please select a choice: 2 ? Please enter Python code for your one-off default value. ? The datetime module is available, so you can do eg datetime.date.today() >>> 37 + Added field base_ptr on basetest.B ? The field 'A.id' does not have a default specified, yet is NOT NULL. ? Since you are removing this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? 3. Disable the backwards migration by raising an exception. ? Please select a choice: 3 - Deleted field id on basetest.A ? The field 'A.name' does not have a default specified, yet is NOT NULL. ? Since you are removing this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? 3. Disable the backwards migration by raising an exception. ? Please select a choice: 3 - Deleted field name on basetest.A ? The field 'A.base_ptr' does not have a default specified, yet is NOT NULL. ? Since you are adding this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? Please select a choice: 2 ? Please enter Python code for your one-off default value. ? The datetime module is available, so you can do eg datetime.date.today() >>> 73 + Added field base_ptr on basetest.A Created 0002_auto__add_base__del_field_b_a_ptr__add_field_b_base_ptr__del_field_a_i.py. You can now apply this migration with: ./manage.py migrate basetest 

I do not know how to answer questions about the default values ​​for B.base_ptr and A.base_ptr. Any constant that I give causes the migration to crash when it starts with this output:

 FATAL ERROR - The following SQL query failed: CREATE TABLE "_south_new_basetest_a" () The error was: near ")": syntax error RuntimeError: Cannot reverse this migration. 'B.a_ptr' and its values cannot be restored. 

This is the result when I use sqlite3, by the way. Using Postgres gives something like this:

 FATAL ERROR - The following SQL query failed: ALTER TABLE "basetest_a" ADD COLUMN "base_ptr_id" integer NOT NULL PRIMARY KEY DEFAULT 73; The error was: could not create unique index "basetest_a_pkey" DETAIL: Key (base_ptr_id)=(73) is duplicated. Error in migration: basetest:0002_auto__add_base__del_field_b_a_ptr__add_field_b_base_ptr__del_field_a_i IntegrityError: could not create unique index "basetest_a_pkey" DETAIL: Key (base_ptr_id)=(73) is duplicated. 

What values ​​should be used for base_ptr to perform this migration? Thanks!

+8
django-south
source share
2 answers

You do this in different phases.

Step 1. Create your base model in code. On models A and B, add base_ptr as zero FK to the base (the base_ptr name base_ptr executed using the base index of the Base class name, adapt your names accordingly). Set db_column='base_ptr' in the new column so you don't add the suffix _id . Do not change parenthood: hold B as a child of A and A as before ( Base does not yet have child classes). Add the migration to make the appropriate changes to the database and run it.

Stage 2. Creating data migration, copying the corresponding data. You should probably copy all the A data into Base , delete the redundant A records (those that served instances of B ), and in the remaining records (both A and B ) copy the identifier to base_ptr . Please note that the child class B uses two tables: the id field comes from table A , and in its own table there is a_ptr field, which is FK to A , so your update operation will be more efficient if you copy values ​​from a_ptr to base_ptr . Make sure that copying to base_ptr occurs after copying to the Base table, so you do not violate the FK restrictions.

Stage 3: now change the models again - delete the explicit base_ptr FK and change the parents as you like and create a third migration (automatic scheme migration). Note that setting the parent element to Base implicitly defines a field with a base_ptr value of base_ptr , so for base_ptr fields base_ptr you only change a field with a zero value to a non-null value, and is not required by default.

You should still request the default value for a_ptr - the implicit FK from B to A , which is deleted when the parent is changed from A to Base ; a default value is required for migration in the opposite direction. You can either do something that cannot reverse migrate, or if you want to support it, add an explicit nullable a_ptr to B , for example the base_ptr columns that you used earlier. This nullable columns can then be deleted in the fourth migration.

+3
source share

If base will not be instantiated on it, you can easily solve the problem using abstract = True prop for class Meta .

Fixed Code:

 from django.db import models class Base(models.Model): name = models.CharField(max_length=10) class Meta: abstract = True class A(Base): pass class B(Base): title = models.CharField(max_length=10) 
+4
source share

All Articles