Django (South): change model application - django

I have defined some models within an application, call it "blog".
djangoproject
/blog
models.py
I now want to change the models location, for example put them here:
djangoproject
/blog
xxx
/all_models
models.py
From the code point of view, this is pretty trivial, but the I guess there will be problems on the database since all the tables Django and South created are now called blog_posts blog_comments, Django relies on this naming convention and I don't want to lose the data already present in the database.
How to do this?

The easiest thing is not to bother changing the tables at all, but the code. Inside the Meta class of each of your models, put the declaration db_table = "blog_tablename", and Django will find them without problems.

You can solve this in two ways.
The first and easier one is to provide a db_table in Meta class of each of your models. The other is create a migration to apply the change.
As far as I know south doesn't support table rename, so you should do it as a three way migration:
Move de models, and create migration (now you have both tables old and new)
Create a data migration and iterate over the former table, copying objects to later
Remove the former model, and create a migration for it.
You can read a little bit more about the second way in south docs

Related

Things to do when remove a model from Django 1.7+

I want to know if any one could give a complete list of things which need to be done when we want to remove a model from Django. I know that a similar question was asked. But it seems to be several years ago, when people were still using South to deal with Database. So I expect an answer for the recent version of Django.
I conclude what I know as follows:
Delete the codes for model from model.py
Make sure that no other file imports this model or uses them
(admin.py, views.py, etc)
Run makemigrations and migrate commands
Since Django doesn't clean database for you, you delete the table of
this model manually from you database
Also note that there is a table called ContentTypes, which keeps
records about the info our your every model. So you need to delete
the record for this model manually (But I don't know how to do it
exactly. Would any one give some explanation?)
These are all the things I know. Is there anything wrong? And did I forget anything? Maybe I'm over-cautious, but I'd like to keep the database clean.
Thanks a lot!
In Django 1.7, this is actually much simpler than you think. Let's say you have an app, books, with two models: Book and BookReview. You want to remove the Book model.
Remove all references to the Book model in your code. For example, remove the ForeignKey('books.Book') field on the BookReview model. There is no need to make a separate migration for this change.
Remove the code for the Book model from books/models.py. Now, create a migration (manage.py makemigrations). If you look at the migration that is generated, it should include a migrations.DeleteModel operation.
Run the auto-generated migration (manage.py migrate), and you should be asked about the relevant ContentType objects that are no longer needed:
Running migrations:
Applying books.0002_auto_20150314_0604... OK
The following content types are stale and need to be deleted:
books | book
Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
You probably do want to delete the content types. If you don't want to be asked for input, you can use manage.py migrate --noinput.
The DeleteModel operation in this migration will drop the books_book table in your database, so you don't have to worry about manually cleaning up at all.

What's the Django 1.7+ equivalent to South's add_ignored_fields()?

Back in previous versions of Django, we all used South to do migrations, and because it wasn't as smart as we might have liked, we sometimes needed to tell it to explicitly ignore some fields because such fields were too complicated for it to handle. We did this with add_ignored_fields and everything pretty much worked.
In our case, we have a "field" on our model that's a subclass of CharField that actually attaches two additional fields to the model via the contribute_to_class method. It's kind of a weird process, but it makes sense for our use case and worked just fine until we upgraded to Django 1.7.
However, with the new migrations system, our use of add_ignored_fields() no longer works and our unit tests are exploding as it tries to create a test database table with these fields. Specifically it says:
django.db.utils.OperationalError: duplicate column name: our_column_name
In this case our_column_name is one of the fields this special CharField subclass creates.
In a perfect world, we could have the migration code handle these fields properly, but I'd settle for the 1.7 version of add_ignored_fields() if it exists.
I should also point out that we've found the explanation for deconstruct in the Django documentation, but I'm afraid that it doesn't make any sense to me. I can't figure out if it applies to this situation or not.
We've discovered this Django ticket that pretty much states that this is a design pattern that the devs aren't supporting, so we're just going to have to rewrite the model to explicitly create the other fields on the model and then reference said fields from the "parent" field a la ImageField.

How to replace a foreignkey with another using a migration

I'd like to replace an existing ForeignKey pointing at my User model with one pointing at a profile model.
The change in the model is:
created_by=models.ForeignKey(settings.AUTH_USER_MODULE)
To:
created_by=models.ForeignKey(settings.PROFILE_MODEL)
The auto-generated migration looks like (with constants subbed in):
migrations.AlterField(
model_name=MODEL,
name='created_by',
field=models.ForeignKey(to=settings.PROFILE_MODEL),
preserve_default=True,
),
I also have ManyToManyFields to deal with as well. What I have in my head is I'd like a function to run on each MODEL object to resolve the user object to the profile object. How would I go about doing this?
The relationship between user and profile is (and vice versa):
User.profile = Profile
Edit: Forgot to mention, if the auto-generated migration is run you get the following error:
ValueError: Lookup failed for model referenced by field
APP1.MODEL.created_by: APP2.PROFILE_MODEL
As I understand now you want to migrate only our app without expecting anything to be changed to the global auth User model. Then it's easy. Migrations work nice with symbolic settings names.
I tried it with Django 1.7. It is possible to switch between settings.AUTH_USER_MODEL and settings.PROFILE_MODEL back and forth without any problem. A migration can be created and applied after every change. The tested model had also a ManyToManyField and mutual relationships between User and Profile.
I see you have APP1 and APP2. Maybe you make migrations for both and they are circular dependent so that a part of other application migration should be applied before the current one migration can be completely applied and vice versa. It can be simplified by spliting a change to more smaller and making automatic migrations after every change so that they are less dependent. A OneToOneField is better than two mutual foreign keys and its reverse relation is even so useful. A foreign key can be changed temporarily to IntegerField(null=True) in the worst case in order to simplify data migration. It is really viable more or less nice.
The question looked nice initially, but the problem should be better specified to be reproducible.
Edited by removing the original text after reading information in comments:

How to rename a django app withouth affecting the database?

In an old project there was an app (let's call it oldapp). I am maintaining this project and I have to rename the app to something else (let's say newapp). The problem is that database tables have been created with the oldapp_ prefix.
I want to avoid to re-creating the database with new prefixes, for obvious reasons.
So, is this possible to rename the app without disturbing the project?
UPDATE: This is one of the errors I am getting:
relation "newapp_dma" does not exist
You can, but I wouldn't advise it. For consistency, migrate your data to the newapp name (possibly using South, or simply do some manual renaming in the database).
If you must, have a look at the db_table in your Meta subclass inside your app classes:
class Something(Model):
class Meta:
db_table = 'newapp_something'
See the docs.
But, this will cause confusion at some point, using inconsistent names.

How do I make changes to a model in Django?

How does Django handle changes to my Model? Or, what help does it offer me to do this?
I am thinking of a situation where I have already have published data to my DB which I don't want to lose, but I need to make changes to my data model - for example, adding extra fields to a particular class, changing the types of fields, etc. My understanding is that syncdb won't ever alter tables that already exist in the DB.
For example, let's say I have the following model:
class Person(models.Model):
name = models.CharField(max_length=200)
phone_number=models.CharField(max_length=200)
hair_colour=CharField(max_length=50)
Things I might want to do to Person off the top of my head:
I wish to add an 'age' field.
I realise I want to use IntegerField instead of CharField for phone_number (whether this is a good idea or not, is out of scope...) - assuming it's possible.
I realise that I no longer wish to define hair_colour 'inline' within Person, because several people share the same hair colour - I wish instead to change this to be a foreign key to some other model.
Whilst I can imagine some of these are tough/impossible for the framework to 'guess' exactly what needs to be done to my data if all I do is update the models.py, I can imagine that there might still be some tooling to help enable it - does it exist?
In particular I imagine there must be some good patterns for option 1.
I'm very new to Django and have no experience with any other ORM-type stuff, which I think this is - I've always been a bit suspicious of ORMs, mainly for the reasons above :)
Django itself will not attempt to modify an already created database table. What you are trying to do is typically called "Migration" and there are a couple of different Database Migration tools available for Django.
South
Schema Migrations
Data Migrations
Backwards Migrations
Nash Vegas
Schema Migrations
Data Migrations
Django Evolution
Schema Migrations
Data Migrations (Unknown)
Backwards Migrations (Unknown)
Of the three South is probably the most widely used but they each have different ways of dealing with migrations. You can see more details on the comparison on Django Packages.
Much of what you're asking about can be done with the django project South. You add it as an INSTALLED_APP. Create a baseline, then as your model changes it creates SQL statements to convert your tables and the rows with-in the tables to the new model format.