Is it valid to reference a foreign key but it is not a primary key in other table? - foreign-keys

I got 3 tables, (1) HOSPITAL table with a primary key HOS_ID, (2) CLINIC table with a primary key CLINIC_IDNO, and (3) DOCTOR table with a primary key DOC_IDNO and a 2 foreign keys (a) HOS_ADDRESS from HOSPITAL table and (b) CLINIC_ADRESS from CLINIC table. Is it a valid foreign keys? How or why is it valid? This is from a "ONLINE CONSULTATION SYSTEM ENTITY RELATIONSHIP DIAGRAM".

Related

Does Django index Autofield / ID keys in PostgreSQL?

Django's docs state that id fields created with AutoField are indexed:
id is indexed by the database and is guaranteed to be unique.
Similarly it applies an index to every FK relationship.
However, in PostgreSQL whilst FKs appear to be indexed, IDs are not. Here's an example:
class TestModelBase(models.Model):
name = models.CharField(max_length=50)
fkfield = models.ForeignKey(TestModelFK, blank=True, null=True,
on_delete=models.CASCADE)
m2mfield = models.ManyToManyField(TestModelM2M, related_name='base_m2m')
This model appears to apply the fkfield index, but not the id autofield. From PGAdmin below:
Am I missing something?
PostgreSQL automatically creates indexes for primary keys. From the docs:
Adding a primary key will automatically create a unique B-tree index on the column or group of columns listed in the primary key, and will force the column(s) to be marked NOT NULL.
It appears that PGAdmin does not show those indexes. This mailing list thread is the best source I could find.

Django foreignKey that doesn't use model's primary key to match to foreign object's primary key?

Lets say I have two models that look like this:
ModelA
uuid (primary key)
id
ModelB
id (primary key)
size
I want to add a field to ModelA. I want this field to be a ForeignKey that joins on ModelA.id == ModelB.id.
I've searched all throughout the Django Docs and have only found how to change the foreign object's field used in the join, but I haven't found a way to change "this" object's field used in the join.
Is it possible to specify a non primary key field from "this" object that is used to create a foreign key by being joined on a matching primary key from the foreign object?
ForeignKey.to_field
ModelA
uuid (primary key) unique=True
id
ModelB
id (primary key)
size
modela = ForeignKey(ModelA, to_field='uuid')

Create index based on foreign key relation

I have a Django model with a ForeignKey relation to another model:
class Book(models.Model):
author = models.ForeignKey(Author)
My Django-generated database PostgreSQL tables are myapp_author and myapp_book, the latter being defined by:
CREATE TABLE "myapp_book" (
"id" serial NOT NULL PRIMARY KEY,
"author_id" integer NOT NULL REFERENCES "myapp_author" ("id") DEFERRABLE INITIALLY DEFERRED,
...
);
What is the PostgreSQL command to create an index on the myapp_book table based on one of the fields of the Author model (let's call it gender)?
This creates a B-tree index named author_indx on table Book based on author_id
CREATE UNIQUE INDEX author_indx ON Book (author_id);
You can read more here

Django Multi-Column Foreign Key

Is is possible to define foreign keys referencing multi columns in another model?
For example one foreign key references a two-column index in the product table, and the SQL statement:
FOREIGN KEY (product_category, product_id) REFERENCES product(category, id)
BTW I've looked into django.contrib.contenttypes and don't think that's the perfect solution for this kind of scenario.
It is not supported yet. There is a ticket and possible ways to handle it if you want to. maybe you could even run custom sql
Multi-Column Primary Key support
Relational database designs use a set of columns as the primary key for a table. When this set includes more than one column, it is known as a “composite” or “compound” primary key. (For more on the terminology, here is an ​article discussing database keys).
Currently Django models only support a single column in this set, denying many designs where the natural primary key of a table is multiple columns. Django currently can't work with these schemas; they must instead introduce a redundant single-column key (a “surrogate” key), forcing applications to make arbitrary and otherwise-unnecessary choices about which key to use for the table in any given instance.
This page discusses how to have Django support these composite primary keys. There are a lot of details to get right here, but done right, it would allow for more flexibility and potential simplicity in data modeling.
Current Status
Current state is that the issue is accepted/assigned and being worked on, and there is a partial implementation at ​http://github.com/dcramer/django-compositepks. The implementation allows having composite primary keys. However, support for composite keys is missing in ForeignKey and RelatedManager. As a consequence, it isn't possible to navigate relationships from models that have a composite primary key.
Discussions:
David Cramer's initial patch
The composite foreign key API design
Ticket
Note - SqlAlchemy allows this as described below and you can use SqlAlchemy to replace Django's ORM
Foreign keys may also be defined at the table level, using the ForeignKeyConstraint object. This object can describe a single- or multi-column foreign key. A multi-column foreign key is known as a composite foreign key, and almost always references a table that has a composite primary key. Below we define a table invoice which has a composite primary key:
invoice = Table('invoice', metadata,
Column('invoice_id', Integer, primary_key=True),
Column('ref_num', Integer, primary_key=True),
Column('description', String(60), nullable=False)
)
And then a table invoice_item with a composite foreign key referencing invoice:
invoice_item = Table('invoice_item', metadata,
Column('item_id', Integer, primary_key=True),
Column('item_name', String(60), nullable=False),
Column('invoice_id', Integer, nullable=False),
Column('ref_num', Integer, nullable=False),
ForeignKeyConstraint(['invoice_id', 'ref_num'], ['invoice.invoice_id', 'invoice.ref_num'])
)
Reference
Yes its possible but you will need to create a composite key when you use multiple column constraint i.e. foreign key or primary key.
For example:
CREATE TABLE Student (
S_num INTEGER,
S_Cate INTEGER,
S_descr CHAR(200),
PRIMARY KEY (S_num, S_Cate))
CREATE TABLE sub_Student (
Ssub_ID INTEGER PRIMARY KEY,
Sref_num INTEGER,
Sref_Cate INTEGER,
sub_descr CHAR(500),
FOREIGN KEY (Sref_num, Sref_Cate) REFERENCES Student
(S_num, S_Cate))
Anyway, you can to create a "Django fixture" like this:
CREATE INDEX product_category_id_id ON product (category_id, id);
To do this, you must to create a file named product.sql on subfolder sql where your model resides. The fixture is loaded on initial syncdb.
#pratik-mandrekar's answer is excellent, but I wanted to point out that even without proper multi-column primary keys; django is able to accommodate queries spanning multi-column foreign keys. Here's an example based on a legacy database who's schema I wasn't permitted to modify:
Given:
from django.db import models
class Account(models.Model):
# Collectively, location_no and occupant_no function as the primary key for Account.
location_no = models.IntegerField()
occupant_no = models.SmallIntegerField()
name = models.CharField(max_length=100)
class Meta:
managed = False
db_table = 'csracct'
unique_together = (('location_no', 'occupant_no'),)
class Call(models.Model):
call_id = models.IntegerField(primary_key=True)
# Collectively, location_no and occupant_no act as a foreign key to Account.
location_no = models.IntegerField()
occupant_no = models.SmallIntegerField()
notes = models.TextField()
class Meta:
managed = False
db_table = 'csrcall'
Here's how you'd use extra() to fetch the 10 most recent calls for accounts with the name 'steve':
calls = Call.extra(
tables = ['csracct'],
where = [
'csracct.location_no=csrcall.location_no',
'csracct.occupant_no=csrcall.occupant_no',
'csracct.name=%s',
],
params = ['steve'],
).order_by('-call_id')[:10]
It's not the most elegant solution, but extra() is part of django's base queryset toolkit; so it plays well with the rest of your django code. Notice how we order_by, and limit/slice the queryset using the usual django methods.

How should a many-to-many table be defined

I'm having some trouble understanding many-to-many fields in Django.
When I create a many-to-many field, ex:
class GlobalPart (Models.model):
...
category_id=models.ManyToManyField(Category, related_name = 'globalpart')
...
and
class Category (Model.model):
...
category = models.CharField(max_length=250)
...
I notice that it created a new table called appname_globalpart_category_id in addition to the appname_globalpart table for the GlobalPart model.
What I'm wondering is, how should the field types in that table be defined. I would think that
there should be at least one foreign key there to relate the fields. But instead there is the primary key for the table, and the other fields are integers (globalpart_id and category_id).
So my question is -- is that normal? Or did I somehow define the many-to-many field incorrectly? And my next question is how would I get all the category_ids associated to a particular GlobalPart?
(1) short answer: Yes this is normal.
Long answer: ManyToMany table will need a foreign key to both Category and GlobalPart tables. Strictly speaking those two foreign keys should be sufficient. The extra pk that you see in there is just for django magic. You can really get away with only those two foreign keys in that table if you manually define the many-to-many table yourself. However if you let django do it for you (by using ManyToManyField) you get this extra pk
(2) I suggest changing your model fields category_id to categories:
class GlobalPart (Models.model):
categories=models.ManyToManyField(Category, related_name = 'globalpart')
This is because, ManyToManyFields refers well to "many" items. This field does not refer to "one" category_id, it refers to all related categories. So when naming it would be natural to name it accordingly.
As for accessing all categories you can do it by accessing the "categories" property. Say if your object instance named global_part, you can access categories like this:
categories = global_part.categories.all()
Instead of all(), you can use filter() or exclude() the same way you use it when querying models.
Here is a link to related django docs
What do you think a foreign key is? It's a field containing values that equate to IDs - usually primary keys - in the "foreign" table. If the other table has integer keys, as most Django tables do, then the foreign key field will be of type integer as well.
Additionally, Django creates constraints so that the database will enforce that the IDs do actually reference valid values in the foreign table. Depending on your database, these might or might not be displayed as part of the field definition.