If I may suggest a better approach: - Make the code as ImageField does (see it's width_field and height_field) - Then focus on writing the migration for this field
Unfortunately, I don't see a way for custom fields to provide hints to the migrations framework. So a field that automatically creates another field is hard to deal with. Another different approach is to use a custom ForeignKey [1]. Then you create only one field, but at the cost of a join. [1] https://github.com/furious-luke/django-address/blob/master/address/models.py#L289 On Wed, Sep 13, 2017 at 2:05 PM, Michal Petrucha <michal.petru...@koniiiik.org> wrote: > On Tue, Sep 12, 2017 at 12:34:26AM -0700, gernot.c...@gmail.com wrote: >> Hi, >> >> I don't know if crossposting with stackoverflow is allowed here but >> essentially my problem is explained >> in >> https://stackoverflow.com/questions/46162104/django-dynamic-model-fields-and-migrations. >> What I want to create is a custom ModelField of type DecimalField, that >> dynamically adds another ModelField (CharField) to the source model. As far >> as I can tell, this is nicely explained >> in >> https://blog.elsdoerfer.name/2008/01/08/fuzzydates-or-one-django-model-field-multiple-database-columns/. >> >> Let's assume I start with a fresh project and app and add this code to >> models.py: >> >> from django.db import models >> from django.db.models import signals >> >> >> _currency_field_name = lambda name: '{}_extension'.format(name) >> >> >> class PriceField(models.DecimalField): >> >> def contribute_to_class(self, cls, name): >> # add the extra currency field (CharField) to the class >> if not cls._meta.abstract: >> currency_field = models.CharField( >> max_length=3, >> editable=False, >> null=True, >> blank=True >> ) >> cls.add_to_class(_currency_field_name(name), currency_field) >> # add the original price field (DecimalField) to the class >> super().contribute_to_class(cls, name) >> >> # TODO: set the descriptor >> # setattr(cls, self.name, FooDescriptor(self)) >> >> >> class FooModel(models.Model): >> >> price = PriceField('agrhhhhh', decimal_places=3, max_digits=10, >> blank=True, null=True) >> >> >> If I then call *./manage.py makemigrations* the following migration file >> for the app is created: >> >> # Generated by Django 1.11.4 on 2017-09-11 18:02 >> from __future__ import unicode_literals >> >> from django.db import migrations, models >> import testing.models >> >> >> class Migration(migrations.Migration): >> >> initial = True >> >> dependencies = [ >> ] >> >> operations = [ >> migrations.CreateModel( >> name='FooModel', >> fields=[ >> ('id', models.AutoField(auto_created=True, primary_key=True, >> serialize=False, verbose_name='ID')), >> ('price', testing.models.PriceField(blank=True, >> decimal_places=3, max_digits=10, null=True, verbose_name='agrhhhhh')), >> ('price_extension', models.CharField(blank=True, >> editable=False, max_length=3, null=True)), >> ], >> ), >> ] >> >> >> All good, as far as I can tell. The problem comes up if I then call >> *./manage.py >> migrate* which errors with the following exception: >> >> ./manage.py migrate testing >> Operations to perform: >> Apply all migrations: testing >> Running migrations: >> Applying testing.0001_initial...Traceback (most recent call last): >> File >> "/usr/local/var/pyenv/versions/stockmanagement-3.6.2/lib/python3.6/site-packages/django/db/backends/utils.py", >> line 63, in execute >> return self.cursor.execute(sql) >> File >> "/usr/local/var/pyenv/versions/stockmanagement-3.6.2/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", >> line 326, in execute >> return Database.Cursor.execute(self, query) >> sqlite3.OperationalError: duplicate column name: price_extension >> > [...] >> As said, I have a fresh project with no exisiting DB and tables so far. Why >> is it complaining that there allready exista a column named >> "price_extension"?. The migration file only contains one field called >> "price_extension"? > > I'm not quite certain about this, but I believe that when the > migration runner reconstructs a version of your FooModel, it iterates > over the list of fields, and adds each of them to the reconstructed > model class one by one. So what happens is that your custom PriceField > gets added, which in turn creates its price_extension field, and then > next thing, the migration runner adds the price_extension field one > more time. So you end up with two instances of the price_extension > field on the same model, and when eventually the create_model schema > operation is executed, it adds each of them as another column to the > table, which is obviously wrong. > > As for what you could do to avoid this situation – I'd suggest that > you add an extra argument to PriceField which tells the field that it > shouldn't create the additional extension field. Then, you implement > the deconstruct method on the field, and include this additional flag. > That way, auto-detected migrations will include the extra argument to > the field instance, and there shouldn't be any duplication of the > extension field on the resulting models reconstructed by migrations. > > Good luck, > > Michal > > -- > You received this message because you are subscribed to the Google Groups > "Django users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to django-users+unsubscr...@googlegroups.com. > To post to this group, send email to django-users@googlegroups.com. > Visit this group at https://groups.google.com/group/django-users. > To view this discussion on the web visit > https://groups.google.com/d/msgid/django-users/20170913120505.GO8762%40koniiiik.org. > For more options, visit https://groups.google.com/d/optout. -- Melvyn Sopacua -- You received this message because you are subscribed to the Google Groups "Django users" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscr...@googlegroups.com. To post to this group, send email to django-users@googlegroups.com. Visit this group at https://groups.google.com/group/django-users. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CA%2Bgw1GUsF%3DjNxMbKUgc3ntg3XPJ59et5cc4sZX36aMb8CP_kMw%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.