Handling Django/Postgres Natural Foreign Key Cascading Updates - python

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.

Related

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

Django: How do I update previously created database entries?

I've got a bunch of previously created database entries of a model class I created, called Equations. I created a new type of model, EquationGroup, and I want to be able to link the existing Equations in the database to newly created EquationGroups. How would I do that?
Update:
I forgot to mention that I've got a ForeignKey relationship in Equation to EquationGroup.
Here is short version of my models.py
class EquationGroup(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Equation(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
group = models.ForeignKey(EquationGroup)
You can create a script that goes through all the equations and attaches them to equations groups. Using Equations.objects.all() you can get an iterable of all of the equations objects.
You can then go through in a for loop and assign each one to the specified Equations group.
Example:
for equation in Using Equations.objects.all():
equation.equationGroup = some_group #based on however you specify what goes in a group.
equation.save()
Do you want to make associations Equation <-> EquationGroup? If so, you should add OneToOne relation field. Look here for examples.
There is many other ways to add relations, like ManyToMany and ForeighKey.
Do you need to associate instances? In this way, you should have data to find related data and save it as OneToOne/ForeignKey.
How do you plan to update the database with the new change to your equation model? If you are using django 1.7+ you can use the built-in migrations. If not you will need to look to something like South. Thirdly you can alter the database by hand.
Anyway, the reason i bring up migrations is because you cant add a null foreign key field. When adding the field to the database table you will need to specify a default FK value, that you can go back and update using #user3154952 suggestion.
Depending on the database you are using, you may be able to remove the foreign-key constraint check temporarily. Allowing you to add the new field to the table without a dummy value, by allowing null.

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

Django ModelField for an object that is the target of ForgeinKeys on other objects

I have some Django objects like this:
class Award(Model):
name = CharField(...)
date = DateTimeField(...)
class Book(Model):
name = CharField(...)
award = ForgeinKey(Award, blank=True)
(i.e. each Book may or may not have one award)
I want to make a form for adding/editing Awards. Assume lots of Books have already been created. Initially I created a ModelForm like this:
class AwardForm(ModelForm):
class Meta:
model = Award
which created a correct form with all the data, however there was no way to add books to the award (or mark this award as applicable to the books that were selected).
I was going to manually add a ModelMultipleChoiceField to the AwardForm (with a queryset for the Books), then add logic to is_valid that would check the Books that were selected, and add something to save to go through the selected Books and set the forgein key on that object to this object.
However, Is there a good 'django' way to do this automatically? i.e. just add a certain field to the Form which will do that all itself? If I were to change the Book.award to be a Award.books manytomany key it would probably do all this automatically, but I like the internal logic of having that as a forgeinkey on the Book.
I was just going to suggest using Inline's but upon re-reading your question the hard part is selecting objects that already exist and linking them to the Award that you are editing.
The way that you have suggested is the first way that I would do it, and the only other way I can think of would involve hacking away at some of the methods on your AwardAdmin to add in the functionality that you desire.
Using the ModelMultipleChoiceField though seems like quite a clean way of doing it to be honest, especially since you shouldn't really need much/any editing of the save method as the field should handle that itself?

Django Data Migration, using South with Inheritance

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.

Categories