I have two models as below:
class Model1(models.model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
filename = models.CharField(max_length=255)
class Model2(models.model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
filename = models.CharField(max_length=255)
I would like to get the related model2 which have the same column value in filename as that of model1.
My solution was to use either Subquery or Extra. The problem with Subquery is that it allows only one column to be queried but what I want is a dict object of all the columns of model2 related to model1. They do not have a foreignkey relation to use select_related. So I tried to use extra but again I am getting the multiple columns error. How can I solve this problem?
My code is as below:
model1_objs = Model1.objects.filter(id=given_id).extra(
select={
"model2_obj": f"SELECT *
FROM model2
WHERE filename = model1.filename
AND id = '{model2_id}'"
}
)
This does not work.
This might not be as efficient as the solution you were thinking of but you could try this:
model1_obj = Model1.objects.get(id=given_id)
model2_objs = Model2.objects.filter(filename=model1_obj.filename)
Note: consider using the get() method to fetch unique objects
Have a look at this: Making queries (Django documentation)
Related
I have a simple question.
I have two models(Waiter and Manager) and they both contains same foreign key to restaurant as:
class Managers(BaseUser):
restaurant = models.ForeignKey(Restaurants, on_delete=models.CASCADE)
class Waiters(BaseUser):
restaurant = models.ForeignKey(Restaurants, on_delete=models.CASCADE)
And restaurant model:
class Restaurants(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(max_length=250)
location = models.CharField(max_length=200)
rating = models.DecimalField(null=True, decimal_places=2, max_digits=5)
So I need to get all waiters that restaurant=managers__restaurant_id.
I think in SQL would be:
select *
From Waiters w
Left outer join Managers m
On w.restaurant_id = m.restaurant_id
Note*
I'm able to query that like below:
manager = Managers.objects.filter(id=request.usr.id)
queryset=Waiters.objects.filter(restaurant=manager.restaurant_id)
But is there any way that i could do it in one query.
But is there any way that i could do it in one query.
This is in one query, but it will work with a subquery is probably not that efficient.
You can however filter in a more compact way with:
Waiters.objects.filter(restaurant__managers=request.user.id)
We can look "through" relations by using double underscores (__). Here we thus are looking for Waiters objects for which it restaurant is related to a Managers object with the given id of request.user.id.
how about this??
queryset = Managers.objects.filter(id=request.usr.id and Waiters.objects.filter(restaurant=manager.restaurant_id))
For the below sample schema
# schema sameple
class A(models.Model):
n = models.ForeignKey(N, on_delete=models.CASCADE)
d = models.ForeignKey(D, on_delete=models.PROTECT)
class N(models.Model):
id = models.AutoField(primary_key=True, editable=False)
d = models.ForeignKey(D, on_delete=models.PROTECT)
class D(models.Model):
dsid = models.CharField(max_length=255, primary_key=True)
class P(models.Model):
id = models.AutoField(primary_key=True, editable=False)
name = models.CharField(max_length=255)
n = models.ForeignKey(N, on_delete=models.CASCADE)
# raw query for the result I want
# SELECT P.name
# FROM P, N, A
# WHERE (P.n_id = N.id
# AND A.n_id = N.id
# AND A.d_id = \'MY_DSID\'
# AND P.name = \'MY_NAME\')
What am I trying to achieve?
Well, I’m trying to find a way somehow be able to write a single queryset which does the same as what the above raw query does. So far I was able to do it by writing two queryset, and use the result from one queryset and then using that queryset I wrote the second one, to get the final DB records. However that’s 2 hits to the DB, and I want to optimize it by just doing everything in one DB hit.
What will be the queryset for this kinda raw query ? or is there a better way to do it ?
Above code is here https://dpaste.org/DZg2
You can archive it using related_name attribute and functions like select_related and prefetch_related.
Assuming the related name for each model will be the model's name and _items, but it is better to have proper model names and then provided meaningful related names. Related name is how you access the model in backward.
This way, you can use this query to get all models in a single DB hit:
A.objects.all().select_related("n", "d", "n__d").prefetch_related("n__p_items")
I edited the code in the pasted site, however, it will expire soon.
i have
class A(models.Model):
field_a = models.OneToOneField(B, on_delete=models.CASCADE)
class B(models.Model):
field_b = models.charField()
how to write the most optimum query here using select_related or something else?
In my use case i need to use both, field_a of model A and field_b of model B somewhere in my code. So can i fetch both the objects in one db hit?
queryset_b = B.objects.get(field_b="some name")
queryset_a = A.objects.get(b=queryset_b).field_a
field_a_of_model = queryset_a
field_b_of_model = queryset_b.field_b
# then some manipulation with both fields
it hits the db twice. Can i do this thing in one db hit using select_related so that it fetches both object at once?
You could use lookup that span relationship
a = A.objects.get(b__field_b="some name")
This would result in single query to database
If you want single query to access fields from b too use select_related()
a = A.objects.select_related().get(b__field_b="some name")
a.field_a.field_b
I am working in Django and I would like to simply merge three tables into one dataset. My models look like so and I have tried variants of the following command:
class Quadrat(models.Model):
quadrat_number = models.IntegerField()
class Capture(models.Model):
quadrat= models.ForeignKey(Quadrat, on_delete=models.DO_NOTHING)
capture_total = models.IntegerField()
class Species(models.Model):
capture = models.ForeignKey(Capture, on_delete=models.DO_NOTHING)
species = models.CharField(max_length=50)
data = pd.DataFrame(list(Species.objects.select_related('Capture_set', 'Quadrat_set').all()
I get an attribute error: Cannot find 'Capture_set' on Species object, 'Capture_set' is an invalid parameter to select_related()
I am probably using select_related wrong.
Any ideas ?
I need to read data for an external database (I cannot modify the table structure) with the two following tables:
Table1
Key (primary key)
Field1
Table2
Key (primary key)
Field2
Is it possible to combine these two tables into a single Django Model that looks like:
Model:
Key (primary key)
Field1
Field2
Additional information:
The keys of the entries in Table2 are a subset of the entries in Table1. So for any entry in Table2 there is a matching entry in Table1, but not viceversa
Work so far:
I have a one model for each table (1 and 2) and Table1 model has a "field2" #property that looks up the corresponding information in the Table2 model.
Also, I can do this at the sql level with the following query:
SELECT
table1.key,
table1.field1,
table2.field2,
FROM
table1
LEFT OUTER JOIN table2
ON table1.key=table2.key
ORDER BY table1.key ASC
===
Solution implementation details update after seeing some of the answers:
From Daniel Roseman's answer
I ended up with two models:
class Model1(models.Model):
key = models.CharField(max_length=100, primary_key=True)
field1 = models.TextField()
class Meta:
managed = False
db_table = 'Table1'
def field2_value(self):
try:
return self.field2.value
except Model2.DoesNotExist:
return None
class Model2(models.Model):
key = models.OneToOneField(Model1, primary_key=True, db_column='key',
related_name='field2')
field2 = models.TextField()
class Meta:
managed = False
db_table = 'Table2'
While it is not exactly what I had originally in mind when I asked this question, it meets my desired use case
Also, here are the corresponding admin classes:
class Model2Admin(admin.StackedInline):
model = Model2
#admin.register(Model1)
class Model1Admin(admin.ModelAdmin):
inlines = (Model2Admin,)
list_display = ('key', 'field1', 'field2_value')
# Loads the related property in one SQL call instead of one call per entry
list_select_related = ('field2',)
Thanks KaaN SARIKAYA for your great answer. I didn't know that I could create a view of the two tables. If I could modify the database structure I would opt for your option.
A better solution would be to keep these as two models, but have the primary key of the second be a OneToOneField to the first.
You cannot merge the model because they are 2 physical tables, django models are tied by the database table names so you couldn't do what you want. If you have the control for those external database tables and have the right to modify them, you should modify the schema first and merge the data. If you don't want to or you cannot modify anything, your current way is OK.
You can create a table_view on sql and make an abstract model on django . Suppose you have 2 pyhsical models and tables.
SQL CODE:
CREATE OR REPLACE VIEW combine_two_model_view AS
SELECT
mt1.name as mt1_name,
mt2.name as mt2_name,
mt1.pub_date as mt1_pub_date
.
.
.
.
FROM modeltable1 mt1
LEFT JOIN modeltable2 mt2 ON mt2.id = mt1.id
ALTER TABLE combine_two_model_view
OWNER TO dbuser;
we created sql view table. now, we will create an abstract model
in your models.py:
class YourModelsCombine(models.Model):
class Meta:
abstract = True
db_table = "combine_two_model_view"
mt1_name = models.CharField(max_length=100)
mt2_name = models.TextField(max_length=2000)
mt1_pub_date = models.DateTimeField("Publish Date", auto_now_add=True)
mt2_pub_date = models.DateField("Updated Date", auto_now=True)
mt1_integer = models.IntegerField(default=0)
#etc
attention: make sure your table view variable names and model variable names must be same. Finally;
in your admin.py:
admin.site.register(YourModelsCombine)
You will see combine of two tables on your django-admin
I hope this helps to you