I have been searching for LEFT JOIN of Django on Stackoverflow, however, most of solution are just too complicated.
My models:
class Voucher(models.Model):
code = models.CharField(unique=True, max_length=255)
delivery_type = models.CharField(max_length=255)
description = models.CharField(max_length=255, blank=True, null=True)
start_at = models.DateTimeField()
end_at = models.DateTimeField()
discount_type = models.CharField(max_length=255)
discount_amount = models.FloatField(blank=True, null=True)
class VoucherCustomer(models.Model):
voucher_code = models.OneToOneField(Voucher, models.DO_NOTHING, db_column='voucher_code', primary_key=True)
customer_id = models.IntegerField()
times_used = models.BigIntegerField(blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
This is what I tried:
from django.db.models.sql.datastructures import Join
#I thought this one is like
from VoucherCustomer left join Voucher
on VoucherCustomer.voucher_code = Voucher.code
j=Join(VoucherCustomer, 'voucher_code',
Voucher,"LEFT JOIN" ,'code', True)
j.objects.filter(voucher_code ='SAIGONS247').values('code', 'delivery_type', 'description', 'times_used').values
However, I got this result in the end:
AttributeError: 'str' object has no attribute 'get_joining_columns'
If you are having a ForeignKey or OneToOne Relationship, you can access the related object's attributes directly using double underscores.
j = VoucherCustomer.objects.filter(voucher_code__code ='SAIGONS247')
j.values('voucher_code__code', 'voucher_code__delivery_type', 'voucher_code__description', 'times_used')
Related
I am joining the ClientDetails, AssignmentTable and CallDetails table to get a view as to which telecaller a particular client has been assigned to and get the latest call details as well. However I am unable to accomplish that using django ORM.
ISSUE:
I am trying to access the fields inside the assignment table and call table but I am getting only the ids and not the other fields.
Question:
How do I extract all the columns from the assignment and call details table which has the client id as 1?
This is the SQL Query that I am trying to come up with:
SELECT t1.uid, t1.phone_number, t1.client_name, t1.base, t1.location, t2.assigner, t2.bpo_agent, t2.cro_agent, t3.bpo_status_id, t3.cro_status_id, t3.agent_id_id
FROM public.bpo_app_clientdetails t1
LEFT JOIN public.bpo_app_assignmentdetails t2 ON t1.uid = t2.client_id_id
LEFT JOIN public.bpo_app_calldetails t3 ON t1.uid = t3.client_id_id;
Below is the model file:
class ClientDetails(models.Model):
uid = models.AutoField(primary_key=True)
phone_number = PhoneNumberField(unique=True)
client_name = models.CharField(max_length=50, blank=True, null=True)
base = models.CharField(max_length=50, blank=True, null=True)
location = models.CharField(max_length=50, blank=True, null=True)
class Meta:
verbose_name_plural = "Client Contact Detail Table"
def __str__(self):
return f"{self.phone_number}, {self.client_name}"
class AssignmentDetails(models.Model):
uid = models.AutoField(primary_key=True)
client_id = models.ForeignKey(
ClientDetails,
on_delete=models.PROTECT,
related_name='assignment_details'
)
date_and_time = models.DateTimeField(auto_now_add=True, blank=True)
assigner = models.ForeignKey(
User,on_delete=models.PROTECT,
related_name='AssignerAgent',
db_column='assigner',
)
bpo_agent = models.ForeignKey(
User,on_delete=models.PROTECT,
related_name='bpoAgent',
db_column='bpo_agent',
)
cro_agent = models.ForeignKey(
User,on_delete=models.PROTECT,
related_name='croAgent',
db_column='cro_agent',
)
class Meta:
verbose_name_plural = "Client Assignment Detail Table"
def __str__(self):
return f"{self.uid}"
class CallDetails(models.Model):
uid = models.AutoField(primary_key=True)
date_and_time = models.DateTimeField(auto_now_add=True, blank=True)
client_id = models.ForeignKey(
ClientDetails,
on_delete=models.PROTECT,
related_name='call_details'
)
agent_id = models.ForeignKey(EmployeeDetails_lk,on_delete=models.PROTECT)
bpo_status = models.ForeignKey(BpoStatus_lk,on_delete=models.PROTECT, blank=True, null=True)
cro_status = models.ForeignKey(CroStatus_lk,on_delete=models.PROTECT, blank=True, null=True)
required_loan_amt = models.CharField(max_length=50, blank=True, null=True)
remarks = models.CharField(max_length=500, blank=True, null=True)
loan_program = models.ForeignKey(LoanProgram_lk, on_delete=models.PROTECT, blank=True, null=True)
disbursement_bank = models.ForeignKey(Banks_lk, on_delete=models.PROTECT, limit_choices_to={'loan_disbursement_status': True}, blank=True, null=True)
class Meta:
verbose_name_plural = "Client Call Detail Table"
def __str__(self):
return f"{self.uid}"
>>> qry=ClientDetails.objects.values('assignment_details','call_details').filter(uid=1)
>>> qry
<QuerySet [{'assignment_details': 1, 'call_details': None}]>
>>> print(a.query)
SELECT "bpo_app_assignmentdetails"."uid", "bpo_app_calldetails"."uid" FROM "bpo_app_clientdetails" LEFT OUTER JOIN "bpo_app_assignmentdetails" ON ("bpo_app_clientdetails"."uid" = "bpo_app_assignmentdetails"."client_id_id") LEFT OUTER JOIN "bpo_app_calldetails" ON ("bpo_app_clientdetails"."uid" = "bpo_app_calldetails"."client_id_id") WHERE "bpo_app_clientdetails"."uid" = 1
You can use prefetch_related() to achieve this. I just use some sample models here for better understanding.
class Company(models.Model):
name = models.CharField(null=True, blank=True, max_length=100)
class Project(models.Model):
name = models.CharField(null=True, blank=True, max_length=100)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
class Employee(models.Model):
name = models.CharField(null=True, blank=True, max_length=100)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
In your views.py function write the below lines to get the desired results
companies = Company.objects.filter(id=1).prefetch_related('project_set', 'employee_set')
for company in companies:
print(company.project_set.values()) # This will print this company projects
print(company.employee_set.values()) # This will print this company employees
Note: If you use related_name in your ForeignKey relationship, make sure that you access with that name instead of model_set inside prefetch_related()
I have 2 models each with foreign keys to 2 tables. I'm trying to join the 1st table to the 3rd.
Here are my models:
Model 1:
class AppBillingBil(models.Model):
id_bil = models.AutoField(primary_key=True)
idtrp_bil = models.ForeignKey(AppTradingPartnerTrp, models.DO_NOTHING, db_column='idtrp_bil', blank=True,
null=True)
idcst_bil = models.ForeignKey(AppCustomerCst, models.DO_NOTHING, db_column='idcst_bil')
idbtp_bil = models.ForeignKey(AppBillingTypeBtp, models.DO_NOTHING, db_column='idbtp_bil')
class Meta:
db_table = 'app_billing_bil'
ordering = ['id_bil']
Model 2:
class AppCustomerCst(models.Model):
id_cst = models.AutoField(primary_key=True)
is_active_cst = models.BooleanField()
name_cst = models.CharField(max_length=50, blank=True, null=True)
Model 2:
class AppTradingPartnerTrp(models.Model):
id_trp = models.AutoField(primary_key=True)
tpid_trp = models.CharField('TPID', max_length=50, blank=True, null=True)
name_trp = models.CharField('Name', max_length=50)
Final Model Needed:
class AppCustomerTpRel(models.Model):
id_rel = models.AutoField(primary_key=True)
idcst_rel = models.ForeignKey(AppCustomerCst, models.DO_NOTHING, db_column='idcst_rel')
idtrp_rel = models.ForeignKey(AppTradingPartnerTrp, models.DO_NOTHING, db_column='idtrp_rel')
cust_vendor_rel = models.CharField(max_length=50, blank=True, null=True)
I need to join on the following criteria:
idtrp_bil__id_trp = idtrp_rel
idcst_bil__id_cst = idcst_rel
And I need to be able to use the cust_vendor_rel field from AppCustomerTpRel in a filter query on AppBillingBil
After reading the docs here: https://docs.djangoproject.com/en/3.0/topics/db/queries/#spanning-multi-valued-relationships I tried this, and was successful:
idcst_bil__appcustomertprel__cust_vendor_rel
I realized I needed to include the target model name in the value grab.
We have the following example models and are wondering if it's possible to select the foreign keys that match during the same query, possibly as an annotation.
class BaseProduct(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
name = models.CharField(max_length=255)
sub_title = models.CharField(max_length=255, blank=True, null=True)
identifier_retailer = models.CharField(max_length=255)
tags = models.CharField(max_length=255, blank=True, null=True)
has_multiple_variants = models.BooleanField(default=False)
class BaseProductVariant(models.Model):
product = models.ForeignKey(BaseProduct)
name = models.CharField(max_length=128, blank=True, null=True)
sub_title = models.CharField(max_length=255, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
description = models.TextField(blank=True, null=True, help_text='Product description')
features = models.TextField(blank=True, null=True, help_text='One feature per line')
content = RedactorField(allow_image_upload=True, allow_file_upload=True, blank=True, null=True, help_text='Use this for rich HTML on featured products')
warranty_string = models.CharField(max_length=255, blank=True, null=True)
identifier_retailer = models.CharField(max_length=255, blank=True, null=True)
identifier_upc = models.CharField(max_length=255, blank=True, null=True)
identifier_model = models.CharField(max_length=255, blank=True, null=True)
We can query the results easily with BaseProduct.objects.filter()... but would like to select the list of matching BaseProductVariant's at the same time, otherwise we have to query the database in an unconventional fashion, and joining in python with prefetch_related on BaseProductVariant.objects.filter(product__in=[]).prefetch_related(product), select_related works on this too, but it a bit slower due to the extra deserialization on each row.
You can use prefetch_related from BaseProduct to prefetch the variants with the related name. You can also use the Prefetch[1] object from django.db.models to control the attribute name where the prefetched variants end up:
from django.db.models import Prefetch
products_with_variants = BaseProduct.objects.all().prefetch_related(
Prefetch('baseproductvariant_set', to_attr='variants'))
for p in products_with_variants:
print(p.variants)
[1] https://docs.djangoproject.com/en/2.2/ref/models/querysets/#django.db.models.Prefetch
I'm working with a legay database so I have to set managed = False, which means I cannot update the database schema.
What I'm trying to do is select branches based on project id. Ideally in branches table it should have a project_id as foreign key but the previous system design is another table (branches_projects) stores this relationship.
I have been able to get around some problems using https://docs.djangoproject.com/en/1.11/topics/db/sql/#django.db.models.Manager.raw. raw() would return an RawQuerySet, which is not ideal.
I wonder if there's a way for me to define a foreign key in my branches table, which is the project_id, but refer/link that to the branches_projects table?
class Branches(models.Model):
name = models.CharField(max_length=128)
branchpoint_str = models.CharField(max_length=255)
dev_lead_id = models.IntegerField(blank=True, null=True)
source = models.CharField(max_length=255)
state = models.CharField(max_length=255)
kind = models.CharField(max_length=255)
desc = models.TextField(blank=True, null=True)
approved = models.IntegerField()
for_customer = models.IntegerField()
deactivated_at = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
codb_id = models.IntegerField(blank=True, null=True)
pm_lead_id = models.IntegerField(blank=True, null=True)
version = models.CharField(max_length=20, blank=True, null=True)
path_id = models.IntegerField(blank=True, null=True)
branchpoint_type = models.CharField(max_length=255, blank=True, null=True)
branchpoint_id = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'branches'
verbose_name_plural = 'Branches'
class Projects(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=40, primary_key=True)
status = models.CharField(max_length=255)
platform = models.CharField(max_length=255)
enabled = models.IntegerField()
path = models.CharField(max_length=128, blank=True, null=True)
tag_prefix = models.CharField(max_length=64, blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
codb_id = models.IntegerField(blank=True, null=True)
template = models.CharField(max_length=64, blank=True, null=True)
image_path = models.CharField(max_length=128, blank=True, null=True)
repository_id = models.IntegerField(blank=True, null=True)
number_scheme = models.CharField(max_length=32)
special_dir = models.CharField(max_length=32, blank=True, null=True)
project_family_id = models.IntegerField()
class Meta:
managed = False
db_table = 'projects'
verbose_name_plural = 'projects'
class BranchesProjects(models.Model):
# project_id = models.IntegerField()
# branch_id = models.IntegerField()
project = models.ForeignKey(Projects, on_delete=models.CASCADE)
branch = models.ForeignKey(Branches, on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'branches_projects'
My current raw query is like this
SELECT br.id, br.name, br.created_at, br.updated_at,
br.branchpoint_str, br.source
FROM branches as br
LEFT JOIN branches_projects as bp
ON br.id = bp.branch_id
WHERE bp.project_id = %s AND source != 'other'
ORDER BY updated_at DESC
I've finally got it working......
In the model, I use the manytomany as following:
class Branches(models.Model):
name = models.CharField(max_length=128)
project = models.ManyToManyField(Projects,
through='BranchesProjects',
related_name='project')
branchpoint_str = models.CharField(max_length=255)
Then to get the same result as my raw sql, i do the following:
def get_sb(project_id):
result = Branches.objects.filter(
project=Projects.objects.get(id=project_id).id,
).exclude(source='other').order_by('-updated_at')
print result.query
return result
While trying to run my second Django 2.1 /Postgres 10 project I got stuck with the following programming error:
ProgrammingError at /admin/vocs_app/subsubscriber/
column sub_subscriber.sub_prev_sst_uid_id does not exist
LINE 1: ...", "sub_subscriber"."sub_next_recharge_datetime", "sub_subsc...
^
HINT: Perhaps you meant to reference the column "sub_subscriber.sub_prev_sst_uid".
I can open the admin app of my application, i.e., 127.0.0.1:8000/admin/vocs_app.
It lists all imported models from my database; to illustrate my case I believe the following classes are sufficient:
(taken from my_site/vocs_app/models.py:)
from django.db import models
class VneVirtualNetwork(models.Model):
vne_uid = models.BigAutoField(primary_key=True)
vne_nop_uid = models.ForeignKey(NopNetworkOperator, models.DO_NOTHING, db_column='vne_nop_uid')
vne_name = models.CharField(max_length=50)
vne_code = models.CharField(max_length=50)
vne_external_id = models.CharField(max_length=100, blank=True, null=True)
vne_idd_code = models.CharField(max_length=5)
vne_sn_length = models.IntegerField()
createdon = models.DateTimeField()
createdby = models.CharField(max_length=150)
createdfrom = models.CharField(max_length=150)
modifiedon = models.DateTimeField()
modifiedby = models.CharField(max_length=150)
modifiedfrom = models.CharField(max_length=150)
class Meta:
managed = False
db_table = 'vne_virtual_network'
class SstSystemStatus(models.Model):
sst_uid = models.BigAutoField(primary_key=True)
sst_name = models.CharField(max_length=50)
sst_description = models.CharField(max_length=100, blank=True, null=True)
startdate = models.DateField(blank=True, null=True)
enddate = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'sst_system_status'
class SubSubscriber(models.Model):
sub_uid = models.BigAutoField(primary_key=True)
sub_vne_uid = models.ForeignKey('VneVirtualNetwork', models.DO_NOTHING, db_column='sub_vne_uid')
sub_rpl_uid = models.ForeignKey(RplRatePlan, models.DO_NOTHING, db_column='sub_rpl_uid')
sub_account_user_id = models.CharField(max_length=100, blank=True, null=True)
sub_external_id = models.CharField(max_length=100)
sub_hzn_uid = models.ForeignKey(HznHomezoneName, models.DO_NOTHING, db_column='sub_hzn_uid')
sub_low_balance_trigger = models.BooleanField()
sub_first_call_datetime = models.DateTimeField(blank=True, null=True)
sub_last_enabled_datetime = models.DateTimeField(blank=True, null=True)
sub_last_disabled_datetime = models.DateTimeField(blank=True, null=True)
sub_last_recharge_datetime = models.DateTimeField(blank=True, null=True)
sub_next_recharge_datetime = models.DateTimeField(blank=True, null=True)
sub_prev_sst_uid = models.ForeignKey(SstSystemStatus, related_name='sub_prev_sst_uid',on_delete=models.DO_NOTHING)
sub_sst_uid = models.ForeignKey(SstSystemStatus, related_name='sub_sst_uid',on_delete=models.DO_NOTHING)
startdatetime = models.DateTimeField()
enddatetime = models.DateTimeField()
createdon = models.DateTimeField()
createdby = models.CharField(max_length=150)
createdfrom = models.CharField(max_length=150)
modifiedon = models.DateTimeField()
modifiedby = models.CharField(max_length=150)
modifiedfrom = models.CharField(max_length=150)
class Meta:
managed = False
db_table = 'sub_subscriber'
class SubSubscriber references foreign key sst_uid of table/class SstSystemStatus twice (previous and current status). It seems Django doesn't like it. Other tables such as VneVirtualNetwork (which contain a "single" foreign key references) don't cause any issue. The admin GUI allows me to display and change their data.
The fault message shows that Django tries to complement the name of field sub_prev_sst_uid with '_id'. If I comment the relevant line in file model.py and try to display the subscriber table it will cause the same error, this time with field sub_sst_uid. How can I prevent Django from appending '_id'?
Thanks in advance for any advice.
By default, Django will use the related name and/or the chosen class field name, add a suffix "_id" and try to locate a column of this name in the database. Unless you explicitly define the db_column as follows:
sub_sst_uid = models.ForeignKey('SstSystemStatus', db_column='sub_sst_uid', related_name='sub_sst_uid', on_delete=models.DO_NOTHING)
sub_prev_sst_uid = models.ForeignKey('SstSystemStatus', db_column='sub_prev_sst_uid', related_name='sub_prev_sst_uid', on_delete=models.DO_NOTHING)