Django inner join methods return unexpected results - python

I am new to Django's ORM and am confused by inner join conventions despite having consulted the documentation and other answers on SO. I have two tables - MyPoints and MyBuffers that are one-to-one related by projectid (no duplicates in either table). My goal is to fetch the radius field using inner join on projectid. Even though I have defined a foreign key, my first attempt at fetching all of the fields from MyBuffers returns nothing from the joined table, and my second attempt at fetching one field from MyBuffers errors. What is wrong with the syntax or methodology here? Should key columns be named differently? Any advice would be greatly appreciated!
models.py
from django.contrib.gis.db import models
class MyBuffers(models.Model):
id = models.BigAutoField(primary_key=True)
projectid = models.CharField(max_length=25, unique=True)
radius = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'my_buffers'
class MyPoints(models.Model):
id = models.BigAutoField(primary_key=True)
projectid = models.ForeignKey('MyBuffers', max_length=25, on_delete=models.DO_NOTHING, to_field='projectid', db_column='projectid')
geog = models.PointField(geography=True, srid=4326)
class Meta:
managed = False
db_table = 'my_points'
views.py
from .models import MyPoints
from .models import MyBuffers
1.Does not return any fields in from the joined MyBuffers table
test = MyPoints.objects.select_related('projectid')
test.first().__dict__
{'_state': <django.db.models.base.ModelState object at 0x7f3e21786520>, 'id': 5808, 'projectid_id': 'foobar1', 'geog': <Point object at 0x7f3e21738910>}
2.Throws an errors
test= MyPoints.objects.select_related('projectid__radius')
test.first().__dict__
django.core.exceptions.FieldError: Non-relational field given in select_related: 'radius'. Choices are: (none)

I think select related only works in foreign keys. So if you are trying to get fields other than foreign keys it will throw an error. Your queryset must be
test= MyPoints.objects.select_related('projectid')
# To get radius
test.first().projectid.radius

from django.contrib.gis.db import models
class MyBuffers(models.Model):
id = models.BigAutoField(primary_key=True)
projectid = models.CharField(max_length=25, unique=True)
radius = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'my_buffers'
class MyPoints(models.Model):
id = models.BigAutoField(primary_key=True)
projectid = models.ForeignKey('MyBuffers',
max_length=25,on_delete=models.CASCADE)
geog = models.PointField(geography=True, srid=4326)
class Meta:
managed = False
db_table = 'my_points'
Try the following code

Related

Django Multi Combination Model Joins

Currently experiencing a problem where my models aren't joining on the foreign key properly,
Doctors can only have one toolset, essentially I need to return [DoctorName, Category, Type, Subtype, Syringe, Bandage]
ToolSetCategoryID, ToolSetTypeID, ToolSetSubTypeID all join on each other's respective tables and all join back to ToolSets
an example of the models would be:
class Doctor(models.Model):
DoctorID = models.AutoField(db_column='DoctorID', primary_key=True)
ToolSetID = models.ForeignKey("ToolSets", db_column='ToolSetID', on_delete=models.CASCADE)
DoctorName = models.CharField(db_column='DoctorName', max_length=100)
class Meta:
managed = False
db_table = 'tblDoctors'
default_permissions = ['add','edit','delete']
class ToolSetCategories(models.Model):
ToolSetCategoryID = models.AutoField(db_column='ToolSetCategoryID', primary_key=True)
Category = models.CharField(db_column='Category', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSetCategories'
default_permissions = ['add','edit','delete']
class ToolSetTypes(models.Model):
ToolSetTypeID = models.AutoField(db_column='ToolSetTypeID', primary_key=True)
ToolSetCategoryID = models.ForeignKey("ToolSetCategories",db_column='ToolSetCategoryID',on_delete=models.CASCADE)
Type = models.CharField(db_column='Type', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSetTypes'
default_permissions = ['add','edit','delete']
class ToolSetSubTypes(models.Model):
ToolSetSubTypeID = models.AutoField(db_column='ToolSetSubTypeID', primary_key=True)
ToolSetTypeID = models.ForeignKey("ToolSetTypes",db_column='ToolSetTypeID',on_delete=models.CASCADE)
ToolSetCategoryID = models.ForeignKey("ToolSetCategories",db_column='ToolSetCategoryID',on_delete=models.CASCADE)
SubType = models.CharField(db_column='SubType', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSetSubTypes'
default_permissions = ['add','edit','delete']
class ToolSets(models.Model):
ToolSetID = models.AutoField(db_column='ToolSetID', primary_key=True)
ToolSetCategoryID = models.ForeignKey("ToolSetCategories",db_column='ToolSetCategoryID',on_delete=models.CASCADE)
ToolSetTypeID = models.ForeignKey("ToolSetTypes",db_column='ToolSetTypeID',on_delete=models.CASCADE)
ToolSetSubTypeID = models.ForeignKey("ToolSetSubTypes",db_column='ToolSetSubTypeID',on_delete=models.CASCADE)
Syringe = models.CharField(db_column='Syringe', max_length=100)
Bandage = models.CharField(db_column='Bandage', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSets'
default_permissions = ['add','edit','delete']
I've found either the fix or a workaround.
I had to filter the conditions I would've used in a join instead of using a join.
You can use Django's built in function F in order to reference a fields dynamic data to filter against (such as filtering field data against another field's data).
Not the best looking solution but is working for now, if anyone can find a more efficient way of doing this please post an answer below.
from django.db.models import F
Doctor.objects.using("test").filter(DoctorID=DoctorID).filter(
ToolSetID__ToolSetTypeID__ToolSetCategoryID=F("ToolSetID__ToolSetCategoryID"),
ToolSetID__ToolSetSubTypeID__ToolSetTypeID=F("ToolSetID__ToolSetTypeID"),
ToolSetID__ToolSetSubTypeID__ToolSetCategoryID=F("ToolSetID__ToolSetCategoryID"),
)

How to prevent Django from renaming key column in model with managed=False?

I have two models in Django with managed=False, because they are generated by SQL.
Django's Querysets based on these models do not generate the SQL queries they should. They add an '_ID' suffix to a column name that I never specified. This causes the queries to fail.
The models:
class MaterialsPerBatch(models.Model):
BATCH = models.CharField(null=False, max_length=255)
MATERIAL = models.ForeignKey('ColumnsPerMaterialStatic', on_delete=models.CASCADE)
class Meta:
db_table = "CDH_MATERIALS_PER_BATCH"
managed = False
class ColumnsPerMaterialStatic(models.Model):
MATERIAL = models.CharField(primary_key=True, null=False, max_length=255)
MATERIAL_LINK = models.CharField(null=False, max_length=255)
class Meta:
db_table = "CDH_COL_PER_MAT_STATIC"
managed = False
I want to filter the first model by BATCH, and find all corresponding second models that share the MATERIAL field.
The query looks like this:
qs = models.MaterialsPerBatch.objects.filter(
BATCH__in=['foo', 'bar']).values('BATCH', 'MATERIAL__MATERIAL_LINK')
I get this error: "django.db.utils.DatabaseError: ORA-00904: CDH_MATERIALS_PER_BATCH"."MATERIAL_ID": invalid identifier"
Inspecting qs.query shows that Django runs the following query in the background:
SELECT "CDH_MATERIALS_PER_BATCH"."BATCH", "CDH_COL_PER_MAT_STATIC"."MATERIAL_LINK"
FROM "CDH_MATERIALS_PER_BATCH"
INNER JOIN "CDH_COL_PER_MAT_STATIC"
ON ("CDH_MATERIALS_PER_BATCH"."MATERIAL_ID" = "CDH_COL_PER_MAT_STATIC"."MATERIAL")
WHERE "CDH_MATERIALS_PER_BATCH"."BATCH" IN ('foo', 'bar')
So the question is, why does Django turn "CDH_MATERIALS_PER_BATCH"."MATERIAL" into "CDH_MATERIALS_PER_BATCH"."MATERIAL_ID"?
I never specified any '_ID' suffix.
How do I tell Django not to add that suffix?
Django allows the db_column name to be specified, so it will not assume the _id is part of the name.
class MaterialsPerBatch(models.Model):
BATCH = models.CharField(null=False, max_length=255, db_column='BATCH')
MATERIAL = models.ForeignKey('ColumnsPerMaterialStatic', on_delete=models.CASCADE)
class Meta:
db_table = "CDH_MATERIALS_PER_BATCH"
managed = False
class ColumnsPerMaterialStatic(models.Model):
MATERIAL = models.CharField(primary_key=True, null=False, max_length=255, db_column='MATERIAL')
MATERIAL_LINK = models.CharField(null=False, max_length=255)
class Meta:
db_table = "CDH_COL_PER_MAT_STATIC"
managed = False
You specify the name of the column with the db_column=… parameter [Django-doc]:
class MaterialsPerBatch(models.Model):
BATCH = models.CharField(null=False, max_length=255)
MATERIAL = models.ForeignKey(
'ColumnsPerMaterialStatic',
on_delete=models.CASCADE,
db_column='MATERIAL'
)
class Meta:
db_table = 'CDH_MATERIALS_PER_BATCH'
managed = False
Normally the names of the fields are written in snake_case, not SCREAMING_SNAKE_CASE, so you might want to consider renaming BATCH and MATERIAL to batch and material and thus use the db_column to specify the name of the database column:
class MaterialsPerBatch(models.Model):
batch = models.CharField(
max_length=255,
db_column='BATCH'
)
material = models.ForeignKey(
'ColumnsPerMaterialStatic',
on_delete=models.CASCADE,
db_column='MATERIAL'
)
# …

Creating new object returns field with None value

I'm trying to create a new object of my model, but keep on getting a value of none for one field.
My models look like this:
class KeyCategory(models.Model):
program = models.ForeignKey('Program', verbose_name='Programm', on_delete=models.CASCADE)
name = models.CharField('Name', max_length=100)
events = models.ManyToManyField('Event', through='EventQuota')
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
ordering = ['name']
unique_together = ("program", "name")
permissions = (
('view_key_category', 'Have access to categories'),
)
class EventQuota(models.Model):
key_category = models.ForeignKey(KeyCategory, on_delete=models.CASCADE, related_name='key_quota')
event = models.ForeignKey('Event', on_delete=models.CASCADE, related_name='key_quota')
quota = models.PositiveIntegerField('Quota', default=0)
class Meta:
verbose_name = 'Quota'
verbose_name_plural = 'Quotas'
unique_together = ('key_category', 'event')
When I try now to create a KeyCategory and my EventQuota, the field "events" for KeyCategory always returns core.Event.None
if program.pk == 1:
for _ in range(0, 2):
key_category = KeyCategory(
program=program,
name=random.choice(self.eventList)
)
key_category.save()
event_quota = EventQuota(
key_category=key_category,
event = random.choice(eventList),
quota = random.randint(0,100)
)
event_quota.save()
Note: the eventList in the random.choice is a queryset list of objects.
I tried to follow Djangos Extra fields on Many-to-Many relationships example, but somehow it seems that I'm missing something here or doing something not the right way? Would appreciate any help! Thanks in regard.
Logging:
import logging
logger = logging.getLogger(__name__)
logger.debug(key_category.events)
When you access key_category.events you are asking for the value of a field (or a single object for foreign key fields). However with a ManyToMany relationship you are asking for multiple objects (a queryset).
Using key_category.events.all() returns the objects related to the key_category, not just a single value.
core.events.all()

Django: ID Null after Save

I Got this Models in models.py:
class Boats(models.Model):
id = models.BigIntegerField(primary_key=True)
created = models.DateTimeField(default=timezone.now, blank=True)
name = models.CharField(max_length=30)
class Meta:
managed = True
db_table = 'boats'
ordering = ['name']
class Clients(models.Model):
id = models.BigIntegerField(primary_key=True)
created = models.DateTimeField(default=timezone.now, blank=True)
code = models.CharField(max_length=100)
class Meta:
managed = True
db_table = 'clients'
ordering = ['name']
==========================
In the views.py; my function do it like that:
f = NewBoatForm(request.POST)
if f.is_valid():
nBoat = f.save()
print 'ID:'+str(nBoat.id)
cBoat = ClientsBoats()
cBoat.client = client
cBoat.boat = nBoat
cBoat.save()
But django fails with this error:
ValueError: save() prohibited to prevent data loss due to unsaved related object 'boat'.
I print the ID but it's Null. Can someOne help me.
You've overridden the primary key field, id, to be something other than an AutoField. Don't do that.
Normally you wouldn't define it at all, and let Django do it automatically. If you are really really sure that a standard integer field is not big enough, you can use BigAutoField. But I doubt you will have more than 2147483647 boats in your database.

IntegrityError at /admin/app/price_list/add/ (1048, "Column 'business_id_id' cannot be null")

i am working with django to create three tables that define a store, it's item list and a price list. In the 'price_list' model i created Foreign key links to the other two tables that i later use to create a compisite primary key using the unique together option. It all validates without an error but when i try to change the 'price_list' via the admin interface, i get the error as stated above in the title to this post. Editing for the other two models goes on without a glitch.
As i understand it, the error is saying the foreign key linked to the primary key of the businesses table is null. How can that be when the value is automatically generated?
Please help
models.py
from django.db import models
class Businesses(models.Model):
business_name = models.CharField(max_length=50, unique=True)
telephone_number = models.CharField(max_length=15)
where = models.ManyToManyField('Location', verbose_name='where?')
def __unicode__(self):
return self.business_name
class Meta:
ordering = ['business_name']
class Location(models.Model):
located_at = models.CharField(max_length=30)
city = models.CharField(max_length=30)
country = models.CharField(max_length=30)
def __unicode__(self):
return u'%s' % (self.located_at)
class Meta:
ordering = ['located_at']
class Item_list(models.Model):
item_name = models.CharField(max_length=50, unique=True)
def __unicode__(self):
return self.item_name
class Meta:
ordering = ['item_name']
class Price_list(models.Model):
price_list_id = models.AutoField(primary_key=True)
price = models.IntegerField(max_length=50)
business_id = models.ForeignKey(Businesses, related_name='businessID')
item_id = models.ForeignKey(Item_list, related_name='ItemID')
business_name = models.ForeignKey(Businesses, to_field='business_name', related_name='businessName')
item_name = models.ForeignKey(Item_list, to_field='item_name', related_name='itemName')
def __unicode__(self):
return u'%s' % self.price
class Meta:
ordering = ['price']
unique_together = (("price_list_id", "business_id", "item_id"),)
admin.py
from django.contrib import admin
from biaSearch.app.models import *
class BusinessAdmin(admin.ModelAdmin):
list_display = ('business_name', 'telephone_number')
search_field = ('business_name')
filter_horizontal = ('where',)
class LocationAdmin(admin.ModelAdmin):
list_display = ('located_at', 'city', 'country')
search_field = ('located_at')
list_filter = ('located_at', 'city')
class Item_listAdmin(admin.ModelAdmin):
list_display = ('item_name',)
class Price_listAdmin(admin.ModelAdmin):
list_display = ('price',)
fields = ('price', 'business_name', 'item_name')
admin.site.register(Businesses, BusinessAdmin)
admin.site.register(Location, LocationAdmin)
admin.site.register(Item_list, Item_listAdmin)
admin.site.register(Price_list, Price_listAdmin)
I suspect you're missing a capital B in the foreign key definition for "businessID" on Price_List.
I'm not sure but I think the whole "related_name='businessID'" argument is unnecessary since you're wanting to create a foreign key to the primary key of the Businesses table.
Try it - it might work!

Categories

Resources