Django/South: Migrating different classes to common base class - django

I have several classes, e.g. Band, Album, Song. Each has the fields title (which is sometimes called name instead) and slug, as well as other, class-specific fields.
I'd like to use South in order to convert them into children of a common base class, Article, which would contain every object's title and slug. However, I'm not sure how South's data migrations are supposed to work in my case; can you help?

You just need to think about the changes South will make to your models, and plan for that.
If Article is an abstract class, South will for the most part disregard it. That is to say all the fields will look as if they are fields directly on the model, pretty much the way you have it now. So, the only changes that will occur are where one model was using name before, it will now have a new title field as well. For this reason, you should leave the name field attached through the schemamigration, then create a datamigration to move its value to the new title field, and finally remove it in another schemamigration.
If Article is a standard base class (multiple-table inheritance), then all your models will get a new OneToOneField to Article. Again, leave all the fields in tact on your models though the schemamigration, then create a datamigration where you will run through each and create an Article instance using the old data on the model and assign that Article instance to the one-to-one field. Then, you can remove the old fields in another schemamigration.

Related

Django CMS copy model instance issue

I have some issue with Django CMS 3.0 plugin model copy_relations function.
Example from documentation
def copy_relations(self, oldinstance):
for associated_item in oldinstance.associated_item.all():
# instance.pk = None; instance.pk.save() is the slightly odd but
# standard Django way of copying a saved model instance
associated_item.pk = None
associated_item.plugin = self
associated_item.save()
is not working and CMS is only modifying instance of plugin model. In this example I need to add associated_item.id = None for it to work.
This is not my first Django CMS project, but is first in 3.0. In previous versions I did follow documentation and everything was ok, but not this time.
The question is: is the documentation not up-to-date or I did something wrong on the way when creating models?
edit:
Here is where I found how to make Django CMS to save a copy of model instance:
Due to how inheritance works, you have to set both ``pk`` and ``id`` to None
I need to do this for all models with copy_relations, and I want to know why is this required.
Obviously in your case associated_item points to a model which inherits from another model. There are two types of model inheritance in Django: abstract and multi-table.
In the case of abstract inheritance, the base model is abstract, i.e. it is not in the database and just serves as a template for the models which inherit from it.
In the case of multi-table inheritance, all of the models are in DB, with submodels linked to the base model via a ForeignKey.
For regular models .pk and .id are one and the same thing (you can even check it with my_model.pk is my_model.id), while for multi-table submodels .id is the id field of the base model, and .pk is the ForeignKey used to connect the submodel to the base model. In fact, the numeric value of .pk and .id is still the same (because .pk points to .id), but nonetheless they are two separate columns in the DB. In my opinion this Django design decision is somewhat inconsistent, but that's what we have.
That's why you need to set both fields to None for Django to lose track of the model and see it as a new model instance.

fields automatically added by django

By default, Django automatically gives each model an id field.
Are there any additional fields django's ORM adds automatically? Perhaps in specific cases?
There are only 2 other situations I can think of where fields are automatically created. One is when sub-classing another model. The sub-class will inherit the parent's fields, see here. The other is a Many-to-Many relationship. For a M2M relationship not only will a field get created but an entire intermediate table. Again, the relevant docs
Also, you can avoid having Django create the id field if you specify primary=True for the field you want to use as the primary key. See here
There are some other model/DB naming conventions as well. For example, the actual database table names will be prefixed with the Django app name that contains them plus an underscore. For example, a model named Author in an app named library will get called library_author. I'm sure there are other examples as well, so this is not an exhaustive list.

Django (South): change model application

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

ManyToMany relationship between inherited model and its parent

Probably easiest to explain with an example:
class Item(models.Model):
# ...
class ComplexItem(Item):
components = models.ManyToManyField(Item, through='ComponentItem', symmetrical=False, related_name='component_of')
class ComponentItem(models.Model):
# ...
item = models.ForeignKey(ComplexItem)
component = models.ForeignKey(Item, related_name='used_in_items
I would like a table of Items, with a name, price etc. Then I would like to define ComplexItems which are Items in their own right, but they require other Items in varying quantities.
The above causes the following exception in the admin app:
<class 'inventory.models.ComponentItem'> has more than 1 ForeignKey to <class 'inventory.models.ComplexItem'>
I need to override instance methods in ComplexItem and generally seperate the behavior from Item and the inheritance makes sense from a pure data view.
Is there some alternative definition of this relationship? I'd also like to avoid needing 'related_name' on both ComponentItem.component and ComplexItem.components.
You need to go back to the drawing board. While it's probably technically possible for a model to both inherit from and simultaneously be composed of another model, it's going to get sticky quick.
Try making ComplexItem just inherit from models.Model like Item does. Bet you that change alone will fix everything.
The model above actually works fine (I think, I haven't tested and decided against it for the moment). However the table generated for ComplexItem only has one column pointing to Item, which is fairly useless.
The functionality of ComponentItem can still be gotten by defining a ManyToMany relationship from Item to 'self' through ComponentItem.
Defining separate behavior is as easy as creating a Proxy model.
The actual error above came from my admin.Inline not being able to pick the correct foreign key to use for a ComponentItem, which can be solved like this.
I may come back to the inheritance above, but this works for now.

Smarter removing objects with many-to-many relationship in Django admin interface

I'd like to remove some object with many-to-many relationship using Django admin interface. Standard removing also removes all related objects and the list of removed objects displayed on confirmation page. But I don't need to remove related objects!
Assume we have ContentTopic and ContentItem:
class ContentTopic(models.Model):
name = models.CharField()
code = models.CharField()
class ContentItem(models.Model):
topic = models.ManyToManyField(ContentTopic, db_index=True,\
blank=True, related_name='content_item')
So, I'd like to remove ContentTopic instance using Django admin, but I don't need remove all related ContentItems. So, confirmation page should display only ContentTopic instance to remove.
What is the best way to handle this?
This happens so, coz its developed to do so.
If you want to change this behaviour, the one way can be over-riding delete method of django.db.models.Model.
This delete() method actually does two things, first gathering a list of all dependent objects and delete them. So here, you can override it, to get that list of dependent objects, iterating over it and set their reference to None, instead of deleting them. And thus deleting the concerned object cleanly.
May be if you want this behavior throughout, you can extend a class from django.db.models.Models, override delete(), and extend all your models from this new class.