Django Data Migration, using South with Inheritance - python

We are migrating the data in several instances of our Django project to a new schema.
The old schema had:
class Group(models.Model)
class User(models.Model)
And the new schema has:
class AccessEntity(models.Model)
class Group(AccessEntity)
class User(AccessEntity)
We are trying to use South to do a data migration for these groups and users. http://south.aeracode.org/docs/tutorial/part3.html
I've gathered that I'll need to use forward rules to specify how to migrate the Users but there are a few issues I've run up against.
The main issue is how to keep the ID of the User/Group the same if I were to create a new User object that extends the AccessEntity class.
Users & Groups are referenced to by objects they own or are assigned to them. If I change their ID that information would be lost. Is there a way of keeping the same ID for an object even though I need it to now extend from AccessEntity?

not sure if I understand your question correctly, but the way multi-table model inheritance works ist that there will be an implicit one-to-one field in the parent and child models. So both User and Group would use an ID field of AccessEntity if AccessEntity has such a field.
If you create AccessEntity such that it has a field ID you can assign to it when you write a forward (data)-migration. That way you can make sure that the AccessEntity gets the right ID.
If have written a longer multi-table inheritance tutorial and it looks like you are trying to do something similar.
And furthermore the answer to this question could also be helpful (note that some things in the original answer does will not work in new versions of django / south, see my tutorial / the answer at the bottom for changes).
What might be a problem in your case is that if you already have data in both User and Groups and the id field is auto-generated, IDs likely not be distinct, e.g. you are likely going to have both a User and a Group with ID==1. This could be a problem if you want to query based on those IDs and of course ID could not be a primary key for AccessEntity then.

Related

How to override model field in Django for library model? [duplicate]

This question already has answers here:
django: modifying/extending 3rd party apps
(3 answers)
Closed 3 months ago.
I need to override library model field in Django. This model is integrated in that library and used there. The changes I need is to add a unique constraint to one of the model fields. But this is not the abstract model so I can't inherit this model as I understand.
The question: is there a way to override usual model field in Django without inheritance?
Not that I'm aware of. I'd be looking at modifying (forking) the "library" model, although there might be issues if it's proprietary 3rd party code for which you do not have source.
The usual thing against concrete inheritance is that it causes a new DB table to be created and thereafter, every query involves a join on a OneToOne field between the two tables. However, I'm wondering whether this happens if you merely add a constraint to an existing field. Might be worth investigating in detail.
Likewise, can you inherit merely to subclass the .save() method, and apply the equivalent to a constraint therein? This isn't bullet-proof, because it's enforced only by the saving of Django objects and can be overridden both by django bulk_update and by anybody accessing the DB table without Django. And again, does it end up creating and joining a second DB table even if it contains no fields at all (except a primary key).
Sorry, more questions than a proper answer. Maybe somebody knows these answers?

Django ORM : model with a list of items from another model

In my Django models, I have two models : one called Site and the other SiteFeature.
Object-wise, it is very clear how this should work : every instance of the Site class should have as property a list containing instances of the SiteFeature class, simply because the SiteFeature objects should only exist in relation to a Site object.
Database-wise, it is also very clear how it should work : the SiteFeature table should contain a not-nullable column referencing the primary key id column of the Site table, with a foreign key.
But in terms of Django ORM, I don't know how to code this.
Based on this question, and this other example, it seems the classical way to proceed works the other way round :
The Site model class contains no ORM model field referencing the SiteFeature list.
Instead, the SiteFeature ORM model class has a ForeignKey field referencing the Site class.
I see there is a way to code this out : by adding a function to the Site model class that searches all the related SiteFeature, and make this function a property (decorator #property):
#property
def site_features(self):
return SiteFeature.objects.filter(site_id=site_id)
But that leaves me doubts :
The proper logic for me would also be that when I save, update or create an instance of the Site class, it would also automatically save / update / create the instances of SiteFeature that are related to it. (same thing for deleting the object, but that can be covered by the on_delete=models.CASCADE parameter of the ForeignKey field).
I could add my own save_with_features / update_with_features / create_with_features methods that cascade all but I am not sure what would happen in case of calls made automatically by Django to the standard save / update / create such as in bulk operations.
This problem seems to basic that I suppose there is already a proper way to do it. How would that be ?
Eventually, I solved the problem with the sitefeature_set Manager.
Reference: https://docs.djangoproject.com/en/3.0/topics/db/queries/#following-relationships-backward

Handling Django/Postgres Natural Foreign Key Cascading Updates

In my application and database, I have Compound and Name models.
# possible names of compound
class Name(models.Model):
compound_name = models.TextField(primary_key=True)
# compound
class Compound(models.Model):
# unique id for the compound
compound_id = models.AutoField(primary_key=True)
# name representing the compound
primary_name = models.ForeignKey(Name, db_column='compound_name', null=True, db_index=True, on_delete=models.PROTECT)
What I'd like to have happen is when I change a name, say 'apsirin' to 'aspirin', this change would cascade to my Compound.
Currently, because of the way Django handles these FK relationships, this doesn't work. If I update a name entry in django, it actually just creates a new entry, and the old compound retains the linkage to the compound.
I've thought of 3 possible solutions, but I'm not quite sure what all the drawbacks are of each.
make a custom 'update' function for my names, that first gets/creates the new name entry, finds all the compounds/other link tables with references to the old name, and then replaces all of the old name values with the new name values, before finally deleting the old name. Possible drawback is that I don't know how this would work if someone was using django admin to update names.
Use signals to intercept an update before it attempted to make the update in the database, and perform something similar to (1)
Create a trigger in postgres to perform something similar to (1) before updates occur. I'm not quite sure if Django would throw an error on this though, since it would be unaware of the trigger.
Its possible I'm just overlooking some basic functionality of models in models.py, and if I am, then hopefully that can solve this issue. But I don't see any kinds of on_update params for the FKs, and I've read that the Django team has labelled this a 'wont fix' issue.
I don't get it yet but:
FK is FK, we just care about FK_id to link it (1)
You can change name of the models and makemigrations (2)
# in SQL, i saw it create model_old, then create a new model and copy (for the function about add and remove field)
# in makemigrations, when change name: it just run the Update query.
When change name, nothing happen to data, it just update the name (3)
When you change the name of a FK (it still just change the name again) (4)
From (1), we can access the data of FK ~> Nothing about change name or something like this can make problem.
So:
Just make sure you create a good models.
To logic should start from model, and fit with your idea.

Django data modeling: Foreignkey or hard coded?

Suppose I have a table of animals which has two attributes name and type, whereas type can be: 'dog', 'cat', etc. Here are two ways to implement this in Django: one where type is a ForeignKey to AnimalType:
class Animal(models.Model):
name = models.CharField(max_length=10)
type = models.ForeignKey(AnimalType)
The other is to just have type as a predefined choice which would be defined in the imported module:
class Animal(models.Model):
name = models.CharField(max_length=10)
type = models.CharField(
max_length=10,
choices=ANIMAL_TYPE_CHOICES
)
The latter (predefined choice) seems more efficient to me, since types will never be dynamically updated by user interaction and if a new type needs to be added it will be added by a developer, i.e. the code would be updated rather than the database.
However, I would like to know if this would be a good/acceptable practice? Or should I waste a separate database table for such a "static" entry and also pay with extra time caused by db accesses?
Thanks.
The first way has the advantage that you don't have to touch the code in order to add a new type of animal.
And of course, someone using your app neither.
Adding a new animal type is something trivial and, for instance, you shouldn´t be messing with a working code deployed on a production server for just add an animal type.
If you´re having problems due to your database is empty at start using the application and because of that you don't have any animal types, well, try Django fixtures: Providing initial data for models
I prefer second way.
If you don't need to edit types from admin panel and always will change it with changes in your code, you do not need to have ForeignKeys and separate table.
In case of ForeignKey, you will have additional integrity check on the database level.
It can be useful if you delete some type and do not want to leave it in DB, for example.
I prefer field choices due to performance reasons. Even if the potential choices increases, as long as the functionality is just a choice selection, there's no reason to create an extra table

How to add many to one relationship with model from external application in django

My django project uses django-helpdesk app.
This app has Ticket model.
My app got a Client model, which should have one to many relationship with ticket- so I could for example list all tickets concerning specific client.
Normally I would add models.ForeignKey(Client) to Ticket
But it's an external app and I don't want to modify it (future update problems etc.).
I wold have no problem with ManyToMany or OneToOne but don't know how to do it with ManyToOne (many tickets from external app to one Client from my app)
Even more hacky solution: You can do the following in the module level code after you Client class:
class Client(models.Model):
...
client = models.ForeignKey(Client, related_name='tickets')
client.contribute_to_class(Ticket, name='client')
I haven't fully tested it (I didn't do any actual database migrations), but the correct descriptors (ReverseSingleRelatedObjectDescriptor for Ticket and ForeignRelatedObjectsDescriptor for Client) get added to the class, and South recognizes the new fields. So far it seems to work just like a regular ForeignKey.
EDIT: Actually not even that hacky. This is exactly how Django sets up foreign keys across classes. It just reverses the process by adding the field when the reverse related class is built. It won't raise an error if any of the original fields on either model is shadowed. Just make sure you don't do that, as it could potentially break your code. Other than that, I don't think there should be any issues.
There are (at least) two ways to accomplish it:
More elegant solution: Use a TicketProfile class which has a one-to-one relation to Ticket, and put the Client foreign key into it.
Hacky solution: Use a many-to-many relation, and manually edit the automatically created table and make ticket_id unique.

Categories