Django Foreign key in another database - mysql

I have two separate Django apps with two different databases. In APP1 I need all of the records from one table which is in APP2 DB. Beside that, one of my APP1 models has a ForeignKey which points to APP2 model, and according to docs this is not possible.
As Django does not support foreign key relationships spanning multiple databases i don't know what to do.
Official docs:
If you have used a router to partition models to different databases,
any foreign key and many-to-many relationships defined by those models
must be internal to a single database.
This is because of referential integrity. In order to maintain a
relationship between two objects, Django needs to know that the
primary key of the related object is valid. If the primary key is
stored on a separate database, it’s not possible to easily evaluate
the validity of a primary key.
As a solution I have thought of merging these two databases, so then these two APPS would use one same database. But then i would have a mess with models because APP1 needs only one table from APP2, and it doesn't need remaining models and DB tables. Also, i'm pretty sure it would make problems (conflicts) while making migrations from these two apps to the one same database.
I'm using Django DB router and this is what i've tried so far:
class Car(models.Model):
_DATABASE = "cars"
color = models.TextField(max_length=1000)
...
class Employee(models.Model):
id = models.IntegerField()
car = models.ForeignKey(Car)
...
It gives me:
django.db.utils.OperationalError: (1054, "Unknow column 'car' in 'employees'")

One DB or Two?
First question to ask your self is it really really necessary to have two different databases? More than one app using the same database is quite common in the PHP world where most people are on shared hosting and the hosting plan allows for only one database.
It's very rare for a website to have sufficient traffic to split the content between two databases. And usually the over head of the router makes it not worth the while. Sharding is almost always better handled with specialized software.
Yep it has to be two!
This is not impossible but just darned hard.
The first thing to do is to change your Car model
class Car(models.Model):
color = models.TextField(max_length=1000)
class Meta:
managed = False
Note that it no longer refers to the second database and it's unmanaged. But you don't have such a table in your database. So you need to create a view
CREATE OR REPLACE VIEW myapp_cars AS SELECT * FROM cars.otherapp_cars
Please make sure to use the exact database and table names to match your existing ones. We are only half way through. You need to create a trigger to insert into the other database. That has been discussed here many times before, so I will not elaborate. Examples
MySQL Trigger to insert data into different DB
How to create trigger to insert data to a database on another server
If you want these steps to be reproducible you will need to add a migration manually.

I thing this problem has nothing to do with django. By design DBMS doesn't allow FK between databases. Maybe you can found some workaround on internet but probable they don't adhere to best practices.
Check Cross Database Relations section at:
https://docs.djangoproject.com/en/3.2/topics/db/multi-db/

What worked for me was setting the ForeignKey db_constraint to False. This way, no constraint is created during the migration, but the correct field type is still created (from the target model's primary key). The database integrity is not enforced, so you have to know what you are doing. The ForeignKey field is indexed by Django regardless of this flag.
Example:
class Employee(models.Model):
car = models.ForeignKey(Car, db_constraint=False)
...
Docs: https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.ForeignKey.db_constraint
This flag has existed at least since version 1.x, so I don't know how it didn't come up before.
Needless to say, you are sacrificing your database constraint, so if a record is deleted on the other database, you will have an invalid reference and this may cause exceptions in Django when accessing the property.
DB Router
You will also need to tell Django that you allow relations between databases. You can do that by adding the following method to your router class:
class MyRouter:
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'default' and obj2._meta.app_label == 'database2':
return True
This function should return True if the router should allow the relation, False if you want to actively deny it, and None for the default behaviour - which is allowing only relations in the same database (which is not what we want). A looser implementation would be to just allow everything (return True).
Docs: https://docs.djangoproject.com/en/4.1/topics/db/multi-db/#allow_relation

Related

Best approach for migrating the "wrong" foreign key to the "correct" foreign key?

For context, I have a Laravel 6 project which made a rather odd choice, to put it mildly, on how to manage relationships when I inherited it.
I have a user object which has it's usual autoincrement id, as well as a "system_id" which is provided by an external system.
For most of the project, relationships involving a user object make use of their "id" field as the foreign key in the belongsTo() part of the relationship which is all well and good.
However, one many-to-many relationship, specifically the one used for the relationship between a user model and a group model, uses the user model's "system_id" field as the foreign key instead of the usual "id" field used everywhere else which is beginning to cause all kinds of development headaches, and is already in production.
So as part of a cleanup project of the system, I intend on migrating the pivot table to use the user model's "id" field. The challenge now is the following:
In a database-agnostic way, how to copy the matching id to the "user_id" foreign key field in the pivot table given a known "system_id".
How will it look in a migration? Is a migration even a good option or should it be done directly in the database instead?
Anything else I should account for?
Is this even a good idea in the first place or should we just live with it?
Obviously, a backup will be made and the whole thing will be tested in a test environment first before it's attempted in production.

Laravel 5.3, MySQL, proper foreign key setup in migrations (one to many, many to many)

what is the proper way to setup the foreign key restrains in following scentarios.
Let say I have models Post, User, Tag.
At the moment my migrations are all set simply as:
For Comment model:
$table->integer('posts_id')->unsigned(); //comment must belong to a post
$table->integer('user_id')->nullable()->unsigned(); //comment can belong to an user
$table->integer('something_id')->unsigned(); // each Something model will have at most 5 to 10 comments in it - this is what is called cardinality if I got it right so the average cardinality is 7.5
and let's say for a many to many relation I have a pivot table between Posts and Tags as:
$table->integer('post_id')->unsigned(); //connecting the post
$table->integer('tag_id')->unsigned(); //connecting the tag
Now I need to index the fields so when my db queries are executed they're executed as fast as possible.
What I would do now is create new migration as:
$table->foreign('posts_id')->references('id')->on('posts');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('something_id')->references('id')->on('somethings');
And the same for the pivot table:
$table->foreign('post_id')->references('id')->on('posts');
$table->foreign('tag_id')->references('id')->on('tags');
My questions are:
Is this the proper way to do this, if not how should I do it instead?
Is there some restrictions, downsides or anything I should be aware of here?
Would I gain anything by doing $table->foreign('something_id')->references('id')->on('somethings'); as the cardinality here is about 7.5? Updating of comments.something_id will never happen in my app, and the ratio of "creation of new something row" to "reading related something row from comment row" would be really low 1:10^5 (rough estimate and in next row of optimizations I'll work on caching the queries so not sure how relevant this information is)
You do not need to create new migration file for defining foreign key.
$table->foreign('posts_id')->references('id')->on('posts');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('something_id')->references('id')->on('somethings');
put those inside comment table migration file. And
$table->foreign('post_id')->references('id')->on('posts');
$table->foreign('tag_id')->references('id')->on('tags');
in pivot table migration file.

How to refer to hardcoded values in code from the database?

In the (MySQL) database, I'm storing a view hierarchy, with each row in a table referring to a single view. There are several types of views, but they're stored in the same table.
In the application code, each type of view has its own class. Each row in the database instantiates one of these classes.
How should I refer to these classes from the database, so the application knows which class to use?
I can think of several possibilities:
Just specify the class name directly in the table, but this has the disadvantage of having to change lots of rows if the class name changes (which can be done in a single query if required).
Have a separate table storing class names, and use foreign keys to point to the row storing the correct class name. In this case, I could forgo having an ID field in this lookup table and instead have the class name as the primary key and target foreign key, and rely on a cascading UPDATE if the class name changes?
Are there better options available?
If I understand correctly you want to maintain an association between view-names and class-names.
Your bullets suggests, that there can be more than one view for the same class and both of your suggestions would work. The second bullet has the advantage that you can change the class name with a single update. But that doesn't buy you much, because as soon as more than just a single class-name changes, i.e. when the association itself changes, you need to update more than one row.
You might even create a separate table, holding this association. This would be the model for an n:m relationship, which is too general, so you'd have to place a unique constraint on the view-name. Essentially this will just factor out the concern of associating view-names with class-names and allow you to change this mechanism entirely without having to mess with your tables (except the one holding this association).
But actually I would not store any of this stuff in the database
(I also find it irritating that view-names are stored in the database and not in the application logic). The fact that there are class-names, should be of no concern to your database. This is application logic and it should be handled there. So what you need is a way to instantiate an object when the view-name is known. This looks like a classic factory to me. After all, if a class name changes, it is a change in the application code and life is easier, when all resulting changes lie in the application code as well.

Possible to Fix a Foreign Key in Yii without having set it up in the Database?

I'm using phpMyAdmin for my database GUI and it's connecting to Yii Framework on my website.
I wish for my products table for instance, to have a foreign key department_id which is a reference to the id field of my departments table. Unfortunately, I don't currently have the facility to set the foreign key up properly in phpMyAdmin, so department_id is just an indexed field. I cannot change the configuration of phpMyAdmin at the moment so it's stuck like it is without a foreign key and relationship facility.
Is there a way to modify the Models of these tables in Yii so they link? I know there is a relations function inside the Model Class file that holds this information:
return array('department_id' => array(self::BELONGS_TO, 'Departments', 'id'),
Could I not just add something similar to the above? Is there more legwork? Is it now fixed (as in static, not corrected) because of phpMyAdmin?
Cheers
If I'm not mistaken, you don't need to have mySql enforcing foreign key relationships for them to still work in Yii. Setting up FK constraints in mySql ensures proper database integrity, but I don't think Yii actually uses that at runtime.
When initially running yiic (of Gii) to build the project I think it looks at the DB to build the right relations in the Model, but it doesn't use them after that.
Yii then uses this knowledge (from yiic) of the table relationships to make your life easier by providing shortcut methods to access relational data, and to ensure you don't violate mySql constraints and get ugly SQL errors, etc. But you can still use Yii relation logic without the underlying SQL constraints. The only problem will be that if Yii messes up and assigns a non-existing FK or something, your database will not catch this error (your data integrity will be more error prone).
To link your products to departments, just make sure you have a department_id field in the Product (which it sounds like you do). Then add a relation rule like so to Product:
'department' => array(self::BELONGS_TO, 'Department', 'department_id'),
And in your Department model:
'products' => array(self::HAS_MANY, 'Product', 'department_id'),
Now you should be able to use the relation like normal:
$myProductModel->department; // returns the model of the Department referenced
$myDepartmentModel->products; // returns the models of all Products in the department
Good luck, and let me know if I am way off base and it doesn't work for you!

Creating an AssociationSetMapping for MySQL with the Entity Framework Designer?

I am trying to build an Entity Framework model for my database schema using EF4 and Visual Studio 2010. Since I am stuck using MySQL as our database (for now), I pretty quickly discovered that since MYISAM tables don't support foreign key constraints, the designer does not have any way to automatically create all the associations for you. It is easy enough to go through the model and build them all manually, but I ran into a problem trying to map a pure join table.
Normally if you are using SQL Server or some other database that supports Foreign Keys, the designer will create all the mappings for you and in the case of pure join tables will create an AssociationSetMapping such that the join table is entirely hidden in the model. Instead you end up with a Many to Many mapping between two two primary entities.
However I am at a loss as to how to create this kind of mapping manually? If I create a Many to Many association between my two entities, I end up with a regular Association, not an AssociationSetMapping. There does not appear to be a way to create one of these in the designer than I can figure out, and to tell it which join table is supposed to be used at the database level.
Am I missing something, and there is a way to do this?
If not, I suppose I have to hack the XML directly, but at this point it is not clear what I need to add to the XML files and where, in order to manually create the association set mapping?
Ok, I can answer this one myself. Once you create a many to many association with the designer, you DON'T set a referential constraint on it, but rather use the Mapping Details window to tell the association what table is the join table, and which keys map to which fields in the database. Simple enough once I figured it out :) But I could not find any good documentation on this.