How to add new field to existing django model postgres - django

Let's suppose I have the following model:
class Test(models.Model):
field_one = models.CharField(max_length=80)
Now, we have created 2-3 Model objects with field_one field.
p1 = Test(field_one="Object1")
p1.save()
p2 = Test(field_one="Object2")
p2.save()
Later, I realised that I need to add another field field_two to my Test model.
class Test(models.Model):
field_one = models.CharField(max_length=80)
field_two = models.IntegerField(default=3)
Now, Doing makemigrations & migrate
and running server.
which will prompt the following error
django.db.utils.ProgrammingError: column mainapp_test.field_two does not exist
I understand that this error occurs due to my 2 existing objects in PostGresDB doesn't have field_two column.
Is there any effective way to add field_two column to my existing objects with some default value? or How to solve this problem?
Django Version: 2.0
Django ORM DB: PostGresql

When you add a field to an existing model, you must either provide a default value in the code, or set it to null/blank = True, or provide a one-off default while migrating.
Since you are providing a default in the code, the migration should run without issues. At least from experience, I've added several BooleanFields with default=False to my existing model with thousands of entries, and I never got a ProgrammingError.
Have you tried shutting down the Postgres backend before running makemigrations and migrate? I would think Django would do this but that's the only thing I can think of. Also, obviously, shut down the Django server if it's still running.

Related

How to apply one specific change to database after 'makemigrations' command?

I added a field to one of my models, but in the 'models' folder I have two other python files which have only View models from which I query views in my database. When I run the makemigrations command, the new migrations file that is created includes also adding these view models to my database as tables (which I don't want to). How can I ignore these changes, and only commit the one addition of a field to an actual table on the database.
I think I maybe have to delete the migrations.CreateModel... in the new migrations file and only keep the migrations.addField... , then run the 'migrate' command. I didn't proceed with this because I'm not sure and maybe it will mess up my database in some way.
Thanks in advance to anyone who can help.
when you make a model for database view you must add meta class managed = false and db_table like this:
class MyViewModel(models.Model):
field: models.CharField(max_length=100)
class Meta:
managed = False
db_table = 'database_view_name'
when you write this and run makemigrations a migration generated contains this model but when you run migrate this migration doesnt change anything on database.
you also can create view using migrations in python. see migrations.RunPython for more details

Django: how to add a field to an existing model without much rewrite of the code

our website lists local city events. It's Django-based so there's a lot of code related to 'event' model. Until now, we work only in one city and so all the events mean to be local.
Now we need to extend the website to another city. This means 'event' model gets a new attribute 'city', and our middleware will set a global value CurrentCity based on geoip.
We need to extend 'event' model so it would filter only records where 'city' attribute equals to CurrentCity value. There is too much code in different views and models working with the 'event' so we can't update each module.
Is there any single place to patch that would make our 'event' model aware of the CurrentCity value?
Depending a lot in your structure and Django version I think you have 2 options.
South
The best one is to install the application "South". I don't know if you already know it or you're using it but I think it should be your first option.
In case you're not using it, you should do this steps:
Install with pip install South
Create your first migration with:
python manage.py schemamigration YOURAPPNAME --initial
You need to fake this migration, because you have already the fullfilled database so you need to do:
python manage.py migrate YOURAPPNAME --fake
Add the new field to the model Event in the file models.py
Generate the new migration to make South create the new field in your database with:
python manage.py schemamigration YOURAPPNAME --auto
Final step, execute the migration created with:
python manage.py migrate YOURAPPNAME
Tips
--initial for the first migration --auto for the rest
The initial migration is faked because you already have tables in your database, if you try to migrate without the fake it will return error "Table already exists"
New Model City
Another option, in case you can't modify your actual Model, or maybe if it's too messy, another option is to generate an externa Model City like this:
class City(models.Model):
event_foreign = models.ForeignKey(Event)
event_many = models.ManyToManyField(Event, blank=True, null=True)
name = models.CharField....
postal_code = models.CharField....
# etc...
I don't know wich is optimal for you, a Foreign Key or a ManyToMany, depends if a City can have more than 1 Event or no, it's your choice.
When you have a model like this you can access from this City model to the Event (because of the ForeignKEy or ManyToMany) but this relation goes also in the other direction, if you have an Event you can get the City/cities related to it I'm gonna show two examples:
Example 1 using Foreign Key
city = City.objects.get(id=1)
city.event # Returns event
event = Event.objects.get(id=1)
event.city # Returns city
Example 2 using Many to Many
city = City.objects.get(id=1)
city.event.all() # Returns a list of events
event = Event.objects.get(id=1)
event.city_set.all() # Returns a list of cities

django model integrity error

so before number field was uncommented i use to get this error Programmingerror: exampeuser.number has no relation to blahblah
class ExampleUser(models.Model):
#number = models.CharField(max_length=15)
phoneID = models.CharField(max_length=30)
verified = models.BooleanField(default=False)
verificationNumber = models.CharField(max_length=5)
now i comment it and syncdb and everything, it gives me an IntegrityError: null value in column "userPhone" violates not-null constraint when i try to save it in the admin website.
userPhone was a field i used a while back and changed it to number.
those errors occur everytime i try to save a model object in the admin site.
it seems that my model is still ineteracting with old changes i made previously. I am new to django.
i also have south on my installed_app and i have not yet migrated or made any configs with it. please help , this is very fustrating
It seems like you're trying to migrate your model, for example, you write a model, then you sync it, then you modify it, change some data types, delete some attributes and add some other and then sync it again.
Django does not support this by default, you need to use South(1) a library that migrates your models through this changes.
Unless you're using django 1.6, in this version they added migrations, here is the documentation. https://docs.djangoproject.com/en/dev/topics/migrations/
Whenever you need to sync a modified model, you need to do it with a migration, not with syncdb.
(1) http://south.aeracode.org/

Adding a "through" table to django field and migrating with South?

Seems like this should be "easy" or at least documented somewhere, I just cant find it.
Lets say I have a model:
class A(models.Model):
users = models.ManyToMany('auth.User', blank=True)
Now I want to migrate to have a through table to add fields to the ManyToMany relation...
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
new_field = models.BooleanField()
class A(models.Model):
users = models.ManyToMany('auth.User', blank=True, through='AUsers')
Then I do:
% ./manage.py schemamigration app --auto
Not totally surprising, it tells me it is going to drop the original auto-created through table and create a new one for AUsers. What's the best practice at this point? Is there a decent way to migrate to the new through table? Do I use db_table in Meta? Do I just not use the through=... right away... then do a schemamigration --auto, then a datamigration to copy the current table (somehow, not sure...) and then add the through relation and let it kill the table?
What's the trick here? Is this really that hard?
You should be able to do this pretty easily.
First of all, make sure that the manual through table that you are creating has the same table name in the database as the one Django originally created automatically.
So, first, let's consider a manual through model before your change:
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
class Meta:
db_table = 'appname_a_user'
That should be functionally (almost) identical to the ManyToManyField you used to have. Actually, you could make an empty migration and apply it, and then use --auto for your changes (but don't).
Now, add your field like you did in your sample code above, and then run ./manage.py schemamigration appname manual_through_table --empty. That will give you an empty migration named ####_manual_through_table.py.
In the migration itself, there will be a forwards and backwards method. Each one needs to be one line each:
def forwards(self, orm):
db.add_column('appname_a_user', 'new_field', self.gf('django.db.models.fields.BooleanField')(default=False))
def backwards(self, orm):
db.delete_column('appname_a_user', 'new_field')
That should get you what you are after.
If anyone comes across this question when trying to do the same thing with the moderns migration framework, here are the steps:
Create a new model class that exactly matches the built-in through table
Use the Meta class to set the table name to match the existing table
Generate a migration, which will create the new table and set it as the through for the field.
Without running that migration, edit it to wrap it in a migrations. SeparateDatabaseAndState migration, where the auto-generated steps are in the state_operations field and the database operations are empty.
Modify your through table, as required, making sure to generate new migrations as normal.
As mentioned in a comment, the first step may be simplified using db.rename_table as described here, which gives this through model:
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
class Meta:
unique_together = (('user', 'a'),)
Then, create a migration with --auto (this way you'll have the names of the DB tables visible), and replace the content with:
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_table('appname_a_user', 'appname_auser')
def backwards(self, orm):
db.rename_table('appname_auser','appname_a_user')
I just applied it in my project without issues.

Allow null in foreign key to user. Django

I have this model
class Vacancy(models.Model):
user = models.ForeignKey(User, null=True, blank=True, default = None)
name = models.CharField(max_length=64)
When in admin i try to creat a vacancy without a user. And it throws an error " club_vacancy.user_id may not be NULL".
Am i doing something wrong?
club_vacancy.user_id may not be NULL
Looks very much like an error from your database, rather than from Django.
It seems most likely that you added null=True after running manage.py syncdb. You'll need to modify your database schema to allow null values in that column.
Aside from South, another option is to use django evolution for schema changes.
http://code.google.com/p/django-evolution/
Install it before making db changes. Then run
python manage.py evolve --hint --execute
Make sure that if you add a new field, you allow nulls (null=True) or else evolve will give you an error message.
you need to reset you database table (since just syncdb does not update fields that are alredy created with null=False)
./manage.py reset your_app
OR if there is some data that you do not want to loose use SQL commands to remove NOT NULL flag