Using South to convert ForeignKey TO ManyToManyField not working out - django

I am using South to change a ForeignKey TO ManyToManyField in one of the models in Django but it is not working out as expected.
# Original Schema
class Item(models.Model):
category = models.ForeignKey(Category, default=default_category)
To be changed to
# Original Schema
class Item(models.Model):
category = models.ManyToManyField(Category, default=default_category)
So after commenting out the ForeignKey line in the model I do,
python manage.py schemamigration affected_model --auto
? The field 'Item.category' 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:
I am confused by this because 1. I have specified a default value which is "default_category" and 2. I am not removing any field I am just changing it to ManyToManyField. My question is how to go ahead in this case? Is there any other trick to make this conversion using South?
BTW I am using South 0.7 and Django 1.1.1
Thanks for the help.

In fact you are removing the field. Foreignkeys are represented by a column in your database that in that case would be named category_id. ManyToMany relationships are represented by a "through" table. With django you can either specif the through table or have one generated for you automatically.
This is a very nontrivial migration and you will need to hand code it. It will require a bit of understanding what the underlying database representation of your model is.
You will require 3 migrations to cleanly do this. First create a schemamigration with a dummy manytomany relationship to hold your data.
Then create a datamigration to copy the foreignkey relationships to your dummy manytomany
Finally create schemamigration to delete the foreignkey and rename the dummy manytomany table.
Steps 2 and 3 will both require you to manually write the migrations. I should emphasize this is a very nontrivial style of migration. However, it's entirely doable you just have to really understand what these relationships mean to the database more than your average migration. If you have little or no data it would be much simpler to just drop the tables and start over fresh with your migrations.

Related

Django max_length where there should not be a max_length

I do not have a deep understanding of Django, anyway not deep enough to overcome a problem that turns up in my application.
In models.py I have a.o. the following definitions:
class Relatiedata(models.Model):
...
getuigen = models.TextField(blank=True, null=True)
...
class Meta:
db_table = 'relatiedata'
Relatiedata.objects = Relatiedata.objects.using('genealogie')
So in the database genealogie, which is not the default database, I have a table relatiedata with a column getuigen that has to contain a text string without a limitation on the length.
Further, I have a user form for mutating records of this table. As usual, the form is populated from a Relatiedata.object and the returned form results in another Relatiedata.object which is saved, thereby updating the database. This works (almost) perfect.
The problem is that in my form it turns out to be impossible to enter a string of length above 600 in the textarea for getuigen. Longer strings are simply cut off. There seems to be sort of a form validation for the length of that field, despite the fact that there is no such limit in the models, nor in the database, nor in the migration files.
This value of 600 comes from earlier, abandoned, implementations of the model, where originally I had a CharField with max_length 600 instead of a TextField.
All remarks are highly welcome.
EDIT
Some additional information. As it is now, the models were created from the already existing database, which is a simple sqlite database with some tables, no relations between the tables. The table relatiedata is a simple table with some textual columns, a little bit too large to show here. Makemigrations and migrate were of course used after the models were constructed, and at the moment makemigrations does not find anything to migrate.
Even worse: when I change the definition:
getuigen = models.CharField(max_length=2000, blank=True, null=True),
makemigrations shows this change and migrate solves it, but it is still impossible to use a string of more than 600 characters in the form. With less, it is OK.
Finally, I can change the value of the field getuigen to strings with more than 600 characters by using SQL. Having done that, in my application the long string turns up correctly in all output where it should be, even in my mutation form. But in the mutation form it cannot be edited because it is too long.
Make migrations and migrate again
python manage.py makemigrations specify_app_name
then
python manage.py migrate specify_app_name

How to add in Django a non-nullable ForeignKey to an existing table?

I have the following models:
class Parent(models.Model):
name = CharField(max_length=80, ...)
class Child(models.Model):
name = CharField(max_length=80, ....)
And I want to add a non-nullable foreign key as shown here:
class Child(models.Model):
name = CharField(max_length=80, ....)
parent = ForeignKey(parent)
Both tables already exists in the database but have no data.
When running makemigrations Django asks for a default value. If I don't want to provide a default value, is it better to perform 2 migrations, the first one with null=True and then run a second one with null=False, before ingesting data in the DDBB?
Thank you for your advice.
Running two migrations would not solve anything. While running second migration you would get again warning that you have to provide a default value.
What is your intention with the existing Child instances? How do you plan to fill the parent column?
If there is no data in both the tables then it does not matter what you select to provide one-off default value just set it to 1 (in case of foreign key it works as ID, but as there is no data it does not matter).
If there is data in those tables then you have to do a 2 step migration first with null=True, then migrate data in migration script and then set it to null=False.

Is there any way to alter add table column in django?

Table name is TestTable. How can I add a new column to the table which is hold in markAtrr variable?
Means how can I perform this query in django?
ALTER TABLE table_name
ADD column_name column_definition;
def createTest(request):
markAttr=request.POST.get('Attr')
obj=TestTable()
I feel you need to read the documentation harder, or work through the tutorials again.
A django Model subclass describes a database table. After you have created it for the first time you run the management commands
./manage.py makemigrations
and if there are no errors to fix,
./manage.py migrate
this latter creates a new table for your app in the database, connected to your model definition.
If you want to add a column to that model (or delete a column, or change the attributes of a column) you editing the model definition to add a field (column definition) and then again makemigrations and upon success, migrate. Your forms, views etc. can then be modified to use the column/field which migration has added or altered.

Is it safe to make a empty field non-empty for a existing database?

Hi, I'm trying to alter my current django project schema, the data migration tool is south. django version is 1.6.9.
It is a existing database so I don't want to mess up with my historical data.
What I'm trying to alter is making a blank=true field non-empty(blank=false)
here is my model class:
class Post(DraftableModel, EditTrackingModel):
title = models.CharField(max_length=120,
verbose_name='headline')
description = models.CharField(max_length=260,
blank=True,
verbose_name='lead-in',
help_text="260 character limit")
/*rest of the code*/
I want change the description from models.CharField(blank=True) to models.CharField(blank=False), which is the default blank value
considering the existence of my database, some of my old Post model do have empty description.
After my migration and change of database schema, will my database be corrupted?
When you try to migrate, django will warn you about those empty fields. so you have few options.
You can add a default value in your model like default="Some Default Value" and django will chose that default value for all empty fields on migration.
You can just send the migrate request and on the terminal, django will ask you to add an one-off value to be added to all the empty fields.
Also you can edit the migrate files and add different values for those empty fields based on conditions you have in mind.
You can write a function to check for all the empty fields for exiting rows and add some data based on some conditions.
Also i highly recommend to make a backup first. Doesn't matter how small the changes are or how good you are in coding.

OneToOne Fields on users causing some ID problems

I have a bit of a problem using django-registration and signals.
The basic setup is that I have a django 1.4.3 setup, with django-south and django-registration (and the db is SQLite for what it's worth).
EDIT: I changed the question a bit because the effect is the same in a shell, so the registration is not in cause (edits are in italic).
I have a one of my model that is related to the User model in the following way:
class MyUserProfile(models.Model):
user = models.OneToOneFiled(User)
#additional fields
I initialized the base using south.
When I do a little sqlall to check the sql that should be in it, I can clearly see:
CREATE TABLE "myApp_myuserprofile" (
"id" integer NOT NULL PRIMARY KEY
"user_id" integer NOT NULL UNIQUE REFERENCES "auth_user" ("id"),
#other fields
)
After that, I wanted to initialize the data if the user activated its account.
So in models.py I put
from django.dispatch import receiver
from registration.signals import user_activated
#Models....
#receiver(user_activated)
def createMyProfile(sender, **kwargs):
currentUser = kwargs['user']
profile = Profile(user = currentUser, #other fields default value)
profile.save()
#And now the reverse relation:
currentUser.myuserprofile = profile
currentUser.save()
While I am in there, everything seems alright, if I print the ids (both for the user and the profile), and if I travel back and forth between the 2, I see something that seems correct.
If I disable this part of the code and do the same kind of initialization using the shell, I get the same result.
But after that, everyhting is wrong.
If I open a shell and import the relevant matters, I have the following for every X value
MyUserProfile.objects.get(pk=X)
#DoesNotExist Exception
User.objects.get(pk=X).myuserprofile.pk
1
MyUserProfile.objects.all()[X].pk
1
Seems a bit weird no?
And now if I go to the sql shell
select id from myApp_myuserprofile;
1
1
1
1
...
So I have a primary column which is filled with the same value all over the place. Which is well... embarrassing to say the least (and does lead to problem, because everyone has a profile with the same Id).
Any idea to what could be the cause of the problem and how I could solve it?
P.S: Note that the foreign key from the related relation are correct and their uniqueness is preserved.
Well looks like the problem was indeed coming from the use of SQLite and South.
The doc states that:
SQLite doesn’t natively support much schema altering at all, but South has workarounds to allow deletion/altering of columns. Unique indexes are still unsupported, however; South will silently ignore any such commands.
Which was (I think) the case, as I hadn't created this relation from the start but with a latter migration. I just reseted the base and migrations and voilà.
See What's the recommended approach to resetting migration history using Django South? for the migrations and a simple ./manage.py reset myApp for the base reset.