Reputation: 2612
I want to add a new column to an already existing table, but I want to give it a default value dependent on already existing data:
e.g. Each record has a start_date
. Now I want to add an open_until
column, and I want to fill it with the value for start_date
of each existing record. (the upcoming records will be able to pick different value)
Is there a friendly way to do this?
Upvotes: 8
Views: 6734
Reputation: 942
In Python 3.8 I first add fields to MyApp
models file and it looks like:
from django.db import models
import datetime
class MyModel(models.Model):
start_date = models.DateField(),
open_until = models.DateField(default=datetime.date.today),
Then, after running manage.py makemigrations
add this lines to new migrations file is created:
def forward(apps, schema_editor):
my_model_objects = apps.get_model('MyApp', 'MyModel').objects.all()
for t in my_model_objects:
t.open_until = t.start_date
t.save()
def reverse(apps, schema_editor):
pass
class Migration(migrations.Migration):
operations = [
" the base operations is here " ,
migrations.RunPython(forward, reverse),
]
Upvotes: 2
Reputation: 2764
As I replied to you, setting a dynamic default value in a database level is not necessary if you are working with a framework :)
The best way, I think, is setting the value of your column in the view before saving the record.
models.py
from django.db import models
class MyModel(models.Model):
start_date = models.DateField(),
open_until = models.DateField(),
forms.py from django.forms import ModelForm
class MyForm(forms.ModelForm):
model = MyModel
fields = ('start_date')
class view
from django.http import HttpResponse
from django.views.generic import CreateView
from .models import MyModel
MyView(CreateView):
form_class = MyForm
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
submitted_form = form.save(commit=False)
submitted_form.open_until = form.cleaned_data["start_date"]
submitted_form.save()
# return an HttpResponse here
For the previous entries, make a view to call just one time, and then loop through all the records and save the values of the new column according the value of the order column.
Something like this:
from django.http import HttpResponse
def set_open_until_values(request)
records = MyModel.objects.all()
for record in records:
record.open_until = record.start_date
record.save()
return HttpResponse("Done!!")
Upvotes: -3
Reputation: 690
You can also do it within South. The only caveat is that you need two steps for that:
A schema migration that adds the open_until column
from django.db import models
import datetime
class MyModel(models.Model):
start_date = models.DateField(),
open_until = models.DateField(default=datetime.date.today),
$ python manage.py schemamigration --auto appname
A data migration that fills existing rows with the value of that other column
$ python manage.py datamigration appname populate_open_until
import datetime
class Migration(DataMigration):
def forwards(self, orm):
"Set open_until value to that of start_date for existing rows"
for t in orm.MyModel.objects.all():
t.open_until = t.start_date
t.save()
def backwards(self, orm):
"Revert back to default"
for t in orm.MyModel.objects.all():
t.open_until = datetime.date.today
t.save()
(optional) In step 1 you can either provide a temporary default value or make it optional and add a 3rd step
Upvotes: 14