Django Join on Composite foreign key - django

I have a relationship as Follows
class Tblrfqvendor(models.Model):
"""RFQ Master for Vendors
This typical represents the same RFQ sent to different people
"""
ven_rfqid = models.ForeignKey(Tblrfqitem, db_column='RFQID', on_delete=models.DO_NOTHING, primary_key=True,related_name="venrfq") # Field name made lowercase.
lineitem = models.IntegerField(db_column='LineItem') # Field name made lowercase.
vendorid = models.CharField(db_column='VendorID', max_length=10) # Field name made lowercase.
class Meta:
managed = False
db_table = 'tblRFQVendor'
constraints = [constraints.UniqueConstraint(fields=['ven_rfqid', 'lineitem', 'vendorid'], name='unique_vendor_rfq')]
ordering = ['-lastrevdate']
class Tblrfqitem(models.Model):
"""A Line Item for a particular Master Vendor RFQ
"""
item_rfqid = models.ForeignKey(Tblrfqmaster, db_column='RFQID', on_delete=models.DO_NOTHING, primary_key=True,related_name='items') # Field name made lowercase.
lineitem = models.IntegerField(db_column='LineItem')
itemid = models.CharField(db_column='ItemID', max_length=100, blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'tblRFQItem'
constraints = [constraints.UniqueConstraint(fields=['item_rfqid','lineitem'], name='unique_item')]
How can I get it so that I can serialize on both lineitem and ven_rfid instead of just the single primary key?

Related

Django Models relation with primary key add extra "_id" to the column

These are my two models, when I try to open City page on Django I get an error: "column city.country_id_id does not exist". I don't know why python adds extra _id there.
class Country(models.Model):
country_id = models.CharField(primary_key=True,max_length=3)
country_name = models.CharField(max_length=30, blank=True, null=True)
class Meta:
managed = False
db_table = 'country'
class City(models.Model):
city_id=models.CharField(primary_key=True,max_length=3)
city_name=models.CharField(max_length=30, blank=True, null=True)
country_id = models.ForeignKey(Country, on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'city'
Because if you construct a foreign key, Django will construct a "twin field" that stores the primary key of the object. The foreign key itself is thus more a "proxy" field that fetches the object.
Therefore you normally do not add an _id suffix to the ForeignKey:
class City(models.Model):
city_id = models.CharField(primary_key=True,max_length=3)
city_name = models.CharField(max_length=30, blank=True, null=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'city'
It however might be better for unmanaged tables, to specify a db_column=… parameter [Djang-doc] in the ForeignKey:
class City(models.Model):
city_id = models.CharField(primary_key=True,max_length=3)
city_name = models.CharField(max_length=30, blank=True, null=True)
country = models.ForeignKey(Country, db_column='country_id', on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'city'
With this parameter you make it explicit how the column is named at the database side.
this is due to Django's behind the scenes magic.
The fields documentation is very clear about that and I highly recommend you read the Foreign Key section in the link below:
https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ForeignKey
Basically, when you want to access the Country reference in the if a City instance, you would do it like this:
city.country_id
I also recommend another naming convention for your Foreign Key fields. Instead of <modelname>_id = models.ForeignKey... just call it <modelname> = models.ForeignKey...
Hope this helps, happy coding

Django - Legacy database tables - Querying 2 one to many tables

I am new to Django and would like some advice on how to query from 3 tables.
I have 3 tables from legacy database mapped in to models (Patient, PrescribedMeds, PrescribedMedsSchedule). We can't change this structure since this will have to remain active while we create the Django application.
1 patient can have many prescribed medication.
1 prescribed medication can have several times in the schedule
Below is the model in django.
models.py
class Patient(models.Model):
patient_name = models.CharField(db_column='patient_name', max_length=50)
dob = models.DateTimeField(db_column='DOB', blank=True, null=True) # Field name made lowercase.
gender = models.CharField(db_column='Gender', max_length=7) # Field name made lowercase.
dateofentry = models.DateTimeField(db_column='DateOfEntry', blank=True, null=True) # Field name made lowercase.
....
....
class Meta:
managed = False
db_table = 'patient'
def __str__(self):
return self.patient_name
class PrescribedMeds(models.Model):
#id = models.AutoField(db_column='ID', primary_key=True) # Field name made lowercase.
patient_id= models.ForeignKey(Patient, models.DO_NOTHING, db_column='patient_id')
med_type = models.SmallIntegerField(db_column='Type') # Field name made lowercase.
name_of_medication = models.CharField(db_column='Name_Of_Medication', max_length=50, blank=True, null=True) # Field name made lowercase.
rxno = models.CharField(db_column='RxNo', max_length=50, blank=True, null=True) # Field name made lowercase.
date_filled = models.DateTimeField(db_column='DateFilled', blank=True, null=True) # Field name made lowercase.
....
....
class Meta:
managed = False
db_table = 'prescribed_meds'
def __str__(self):
return str(self.id) + ", " + self.name_of_medication + ", " + str(self.childno)
class PrescribedMedsSchedule(models.Model):
#id = models.AutoField(db_column='ID', primary_key=True) # Field name made lowercase.
prescribed_meds_id = models.ForeignKey(PrescribedMeds, models.DO_NOTHING, db_column='prescribed_meds_ID') # Field name made lowercase.
medication_date = models.DateField()
medication_time = models.DateTimeField()
quantity = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
form = models.CharField(max_length=1, blank=True, null=True)
class Meta:
managed = False
db_table = 'prescribed_meds_schedule'
I am trying to get the right syntax in Django to display data from the 3 columns (Prescribed_Meds.ID, PrescribedMeds.name_of_medication, Patient.patient_name, PrescribedMedsSchedule.medication_date, PrescribedMedsSchedule.medication_time).
In SQL the query would be
SELECT prescribed_meds.ID, prescribed_meds.Name_Of_Medication, patient.patient_name, prescribed_meds_schedule.medication_date, prescribed_meds_schedule.medication_time
FROM prescribed_meds_schedule
INNER JOIN (prescribed_meds INNER JOIN patient ON prescribed_meds.patient_id = patient.id) ON prescribed_meds_schedule.prescribed_meds_ID = prescribed_meds.ID;
What would be the correct query in Django? I am having an issue since there is no relationship from PrescribedMedsSchedule to Patient table.
I have tried the following:
my_obj = PrescribedMedsSchedule.objects.all().selected_related(
'prescribed_meds_id'
).prefetch_related('PrescribedMeds__patient_id')
However, this query does not bring up the Patient table/model.
Any advice would be appreciated.
It's worth starting with a note on what django does internally with a FK. You have a suffix in your models of _id but django will do this to the database column automatically. So in your model you could have an easier to read field, patient = models.ForeignKey(Patient) and it will be patient_id in the database.
select_related follows foreign-key relationships, so you're right to do that. If you want to INNER join you should follow the foreign-keys. In your case use the
double-underscore to get through the models:
PrescribedMedsSchedule.objects.all().select_related(
'prescribed_meds_id'
).select_related('prescribed_meds_id__patient_id')
Or you could use 2 queries and use the id values from PrescribedMeds to then query PrescribedMedsSchedule. That would be something like;
meds = PrescribedMeds.objects.all().select_related('patient_id')
med_ids = meds.values_list('id', flat=True)
schedules = PrescribedMedsSchedule.objects.filter(prescribed_meds_id__in=med_ids)

Django join different tables

This is what i got in the models
class SFE(models.Model):
snpid = models.ForeignKey(Snps, models.DO_NOTHING, db_column='SNPID', primary_key=True) # Field name made lowercase.
elementid = models.ForeignKey(Functionalelement, models.DO_NOTHING, db_column='ElementID') # Field name made lowercase.
celllineid = models.ForeignKey(Celllines, models.DO_NOTHING, db_column='CELLLINEID') # Field name made lowercase.
countexperiments = models.PositiveIntegerField(db_column='countExperiments') # Field name made lowercase.
filetype = models.CharField(db_column='fileType', max_length=10) # Field name made lowercase.
class Meta:
managed = False
db_table = 'SNPs_FunctionalElement'
unique_together = (('snpid', 'elementid', 'celllineid', 'filetype'),)
def __str__(self):
return str(str(self.snpid) + str(self.elementid) + str(self.celllineid) + str(self.filetype))
class Functionalelement(models.Model):
elementid = models.AutoField(db_column='ElementID', primary_key=True) # Field name made lowercase.
name = models.CharField(unique=True, max_length=55)
class Meta:
managed = False
db_table = 'FunctionalElement'
def __str__(self):
return str(self.elementid)
class Snps(models.Model):
snpid = models.AutoField(db_column='SNPID', primary_key=True) # Field name made lowercase.
rsid = models.CharField(unique=True, max_length=20)
chrom = models.CharField(max_length=5)
pos = models.PositiveIntegerField()
ref = models.CharField(max_length=1)
alt = models.CharField(max_length=1)
maf1000genomes = models.FloatField(blank=True, null=True)
maftopmed = models.FloatField(db_column='mafTOPMed', blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'SNPs'
def __str__(self):
return str(self.snpid)
Now i want to join FunctionalElement with SFE in order to retrieve the field FunctionalElement.name given a specific SFE.snpid.
I tried with SFE.objects.select_related('elementid__name') but i know it's wrong and i can't understand how to work with django ORM
To get a simple object you need to do: a = SFE.objects.get(snpid=THE_SPECIFICSNPID) later you can access to all the related objects, for example: a.elementid.name will return what you want.
The Django ORM take retrieve the object for you, that is because "lazzy loading". That means that if you need a related object later Django will get it for you, of course, it need to do another query and to avoid that you need to call the method select_related
Summarizing:
To get the name you can do:
name = SFE.objects.get(snpid=THE_SPECIFICSNPID).select_related('elementid').elementid.name
It should works

How to set foreign key to a field of another model?

I want to set a foreign key to a field of another model.
I have tried Foreign Key to_field='field_name'
class Banks(models.Model):
name = models.TextField()
id = models.IntegerField(unique=True)
class Meta:
db_table = 'banks'
class Branches(models.Model):
ifsc = models.CharField(max_length=20, null=False)
bank_id = models.ForeignKey(Banks, to_field='id', on_delete=models.CASCADE)
branch = models.CharField(max_length=50)```
ProgrammingError: column branches.id does not exist
LINE 1: SELECT "branches"."id", "branches"."ifsc", "branches"."bank_...
Just add unique=True in the name column, in the Banks model.
class Banks(models.Model):
name = models.TextField(unique=True) # Just add unique=True
id = models.IntegerField(unique=True, primary_key=True)
class Meta:
db_table = 'banks'
class Branches(models.Model):
ifsc = models.CharField(max_length=20, null=False)
bank_id = models.ForeignKey(Banks, to_field='id', on_delete=models.CASCADE) # Now it will work
branch = models.CharField(max_length=50)
This problem is not caused by the foreign key. The error is happening in the Branches model, which presumably also has a db_table Meta attribute and is based on a legacy table.
You must define a primary key for your models. If you don't, Django will do so automatically and call it id. In the case of your Banks model, you should set that id field as primary_key=True - or indeed remove it completely, since that is the default. You need to find a suitable pk for Branches as well and declare it in the field.
For your actual question, you don't need to do anything; Django will automatically set the FK to point to the PK of the target model.
class Bank(models.Model):
# removed `id` as that is the default PK
name = models.TextField()
class Meta:
db_table = 'banks'
class Branch(models.Model):
ifsc = models.CharField(max_length=20, primary_key=True) # assume this is the PK
bank = models.ForeignKey(Bank, on_delete=models.CASCADE)
branch = models.CharField(max_length=50)
class Meta:
db_table = 'branches'
Note also, since these are legacy tables you probably want to add managed = False to both Meta classes. And as suggested by AKX, it is Django style to make model names singular; you can do that without affecting the table name since that is declared explicitly.

django model instance could not get the field after save

Here is my code:
models.py
class TblUser(models.Model):
uid = models.IntegerField(primary_key=True, db_column='ID') # Field name made lowercase.
username = models.CharField(max_length=3072, db_column='UserName', blank=True) # Field name made lowercase.
password = models.CharField(max_length=3072, db_column='PassWord', blank=True) # Field name made lowercase.
datesstart = models.DateTimeField(null=True, db_column='datesStart', blank=True) # Field name made lowercase.
datesend = models.DateTimeField(null=True, db_column='datesEnd', blank=True) # Field name made lowercase.
num = models.IntegerField(null=True, db_column='Num', blank=True) # Field name made lowercase.
power = models.IntegerField(null=True, db_column='Power', blank=True) # Field name made lowercase.
email = models.CharField(max_length=12288, blank=True)
class Meta:
db_table = u'tbl_user'
def __unicode__(self):
return '%d--%s--%d'%(self.uid,self.username,self.power)
views.py
from app.models import TblUser
def appendUser(self,name,pwd):
#I add a new user,and the primary_key:uid is autoincrement in datebase
user = TblUser.objects.create(username=name,password=pwd)
print user.uid#None
When I call the appendUser(),it will insert a new record into datebase, and the user(TblUser's instance) only have two valid fields(username,password), the other is empty.
How can I get the user.uid because I want handle other things by using it?
You should use an AutoField not an IntegerField for the pk: https://docs.djangoproject.com/en/1.4/ref/models/fields/#autofield
You might want to read this post, different approach using ModelForm:
Get Primary Key after Saving a ModelForm in Django