django query with ...objects.raw - django

How I can make a query with two models using this
model.objects.raw(...)
and into the sql query has the INNER JOIN with the another model(table) this is possible
model.objects.raw('
SELECT establecimiento.nombre, categoria.titulo
FROM establecimiento INNER JOIN
categoria ON establecimiento.categoria = categoria.id')
I need print the establecimiento's name with his categoria's name
class Establecimiento(models.Model):
nombre = models.CharField(max_length = 140)
categoria = models.ForeignKey(Categoria)
ciudad = models.ForeignKey(Ciudad)
def __unicode__(self):
return self.nombre
class Categoria(models.Model):
titulo = models.CharField(max_length = 140)

Fetching objects from the ORM automatically does any joins required and will return objects (instances of the models) which you can use to follow relationships.
If you simply fetch all your Establecimiento objects, you can access the related Categoria objects, like this:
all_objects = Establecimiento.objects.all()
for obj in all_objects:
print('Number: {} Category: {}'.format(obj.nombre, obj.categoria.titulo))
Or, if you want to fetch only those two specific properties, use values, like this:
all_objects = Establecimiento.objects.values('nombre','ciudad__titulo')
for obj in all_objects:
print('Number: {} Category: {}'.fromat(obj['nombre'],obj['ciudad__titulo']))

Related

How to get the related child type when the parent can have multiple child type?

Let's say I have 3 models like this:
class Parent(models.model):
CHILD_CHOICES = (
('ChildA', 'Child A'),
('ChildB', 'Child B'),
)
name = models.CharField(max_length=50)
child_type = models.CharField(choices=CHILD_CHOICES, max_length=25, blank=True)
class ChildA(models.model):
parent = OneToOneField('Parent',related_name='child_a',blank=True, null=True)
class ChildB(models.model):
parent = OneToOneField('Parent',related_name='child_b',blank=True, null=True)
When the parent is created the associated child model is created (depending on the child_type field).
How to retrieve the according Child type from a Parent in the Parent view in order to map a custom Form to the right child? (for editing the Parent and the right Child Type in the same view)
(In my real scenario I have 10 different child type)
Two options come to mind.
1) You can use the get_model() method, which is probably cleaner, to find the correct child.
2) You can do an IF statement in the middle of of a queryset. I have hardcoded the example, but you can probably pull the child_type variable all the way thru, and use annotate. I'm really just showing the example for how you can break up and tweak your queries with multiple related models.
Example 1:
from django.db.models import get_model
class ParentForm(forms.Form): # or view
***code here**
parent_model = Parent.objects.get(pk=1)
# Get model name from CHILD_CHOICES.
child_relation = [k for k, v in Parent.CHILD_CHOICES if v == parent_model.child_type]
child_model = get_model('app_name', child_relation)
child_model.objects.all()
***more code here***
Example 2: (occasionally handy depending on your setup):
class ParentManager(models.Manager):
def get_child_info(self, child_type):
try:
query = super(ParentManager, self).get_queryset()
# This line only works if the models have the same fields, otherwise do the below.
query = query.values('id', 'name')
if child_type == 'Child A':
# Do this if your child models have different fields.
query = query.values('id', 'name', 'unique_childA_field')
query = query.select_related('child_a').all()
elif child_type == 'Child B':
# Do this if your child models have different fields.
query = query.values('id', 'name', 'unique_childB_field')
query = query.select_related('child_b').all()
except Exception:
query = None
return query
class Parent(models.model):
CHILD_CHOICES = (
('ChildA', 'Child A'),
('ChildB', 'Child B'),
)
name = models.CharField(max_lenght=50)
child_type = models.CharField(choices=CHILD_CHOICES, max_length=25, blank=True)
objects = ParentManager()
then in your view or form:
class ParentForm(forms.Form): # or view
***code here**
child_info = Parent.objects.get_child_info('Child A')
***more code here***

Django not in other table (Not using id)

I have:
Models:
class Category(models.Model):
description = models.CharField(unique=True, max_length=200)
myfield = models.CharField(unique=True, max_length=200)
class Car(models.Model):
categorys = models.ManyToManyField(Category)
myfield = models.CharField(unique=True, max_length=200)
I am trying to execute the following query:
Car.objects.filter(category__id = c.id )
.exclude(myfield__in =
Category.objects.all()
.only('myfield')
)
.order_by("id")
.first()
I expected to find a result like this:
SELECT ...
FROM `myapp_car`
INNER JOIN `myapp_car_categorys`
ON (`myapp_car`.`id` = `myapp_car_categorys`.`car_id`)
WHERE ( ...
AND NOT (`myapp_car`.`myfield` IN
(SELECT `myapp_category`.`myfield` FROM `myapp_category`)))
ORDER BY `myapp_car`.`id` ASC LIMIT 1;
But i find:
SELECT ...
FROM `myapp_car`
INNER JOIN `myapp_car_categorys`
ON (`myapp_car`.`id` = `myapp_car_categorys`.`car_id`)
WHERE ( ...
AND NOT (`myapp_car`.`myfield` IN
(SELECT `myapp_category`.`id` FROM `myapp_category`)))
ORDER BY `myapp_car`.`id` ASC LIMIT 1;
I need use myfield in select, not id:
(SELECT `myapp_category`.`myfield` FROM `myapp_category`)
How would I get this result?
Even if you use only it will return the objects of Category model. So if you use it in in filtering the category objects will be used for filtering and for SQL the ids of those category objects.
If you need to filter on the myfield values on all the category objects, then use values instead:
....exclude(myfield__in=Category.objects.values('myfield'))

django saving list of foreignkey objects to m2m field w/ through model with ordering

I have a html table w/ javascript that allows ordering of rows which passes the track_id list and row order list through a form on POST.
I just added the class PlaylistTrack using through for the model so I can add ordering to the tracks.m2m field. The view I have below works before I added the through model, but now I am not sure how I should save a list of tracks w/ its associated order number since I can't use add() and I must use create(). How should I use create() in my view to save a list of track_id's and associate the order number w/ list? Can I use bulk_create?
models.py:
class Playlist(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
name = models.CharField(max_length=50)
tracks = models.ManyToManyField(Track, through='PlaylistTrack')
def __str__(self):
return self.name
class PlaylistTrack(models.Model):
track = models.ForeignKey(Track)
playlist = models.ForeignKey(Playlist)
order = models.PositiveIntegerField()
class Meta:
ordering = ['order']
views.py:
def add_track(request):
active_playlist = Playlist.objects.get(id=request.POST.get('playlist_id'))
add_tracks = request.POST.getlist('track_id')
if request.POST.get('playlist_id'):
active_playlist.tracks.add(*add_tracks) # how to use create or bulk_create?
return redirect("playlists:list_playlists")
Ozgur's answer has you mostly covered. However, you do NOT need to fetch Playlist or Track instances from the database and you CAN use bulk_create:
def add_track(request):
playlist_id = request.POST.get('playlist_id')
track_ids = enumerate(request.POST.getlist('track_id'), start=1)
PlaylistTrack.objects.bulk_create([
PlaylistTrack(playlist_id=playlist_id, track_id=track_id, order=i)
for i, track_id in track_ids
])
return redirect("playlists:list_playlists")
This reduces the entire procedure to a single db operation where you had (1 + 2n) operations before (n being the number of tracks).
You need to fetch Track objects that will be added:
Track.objects.filter(pk__in=add_tracks)
--
Since order field must be populated, you can't use .add() on M2M field. You gotta create objects by yourself:
def add_track(request):
playlist = Playlist.objects.get(id=request.POST.get('playlist_id'))
for i, track_id in enumerate(request.POST.getlist('track_id'), start=1):
track = Track.objects.get(pk=track_id)
PlaylistTrack.objects.create(track=track, playlist=playlist, order=i)
return redirect("playlists:list_playlists")

How to implement "contain any" in flask/sqlalchemy?

The contains operation in SQLAlchemy only accepts one Model object instead of a list of objects. If I want to create a filter that accepts containing any of a group of objects, is there a more SQL-style way than creating multiple filters using contains and combining them with union?
For example, see the following code:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(100))
son = db.relationship('Son', backref = 'parent', lazy = 'dynamic')
def __repr__(self):
return '<"%s">' % self.name
class Son(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(1000))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<"%s">' % self.name
db.create_all()
son1 = Son(name = '1 a 1')
son2 = Son(name = '1 b 1')
son3 = Son(name = '1 c 1')
son4 = Son(name = '2 a 2')
son5 = Son(name = '2 b 2')
user0 = User(name = 'w1')
user1 = User(name = 'w2')
user2 = User(name = 'w3')
user0.son.append(son1)
user0.son.append(son2)
user1.son.append(son3)
user1.son.append(son4)
user2.son.append(son5)
db.session.add(son1)
db.session.add(son2)
db.session.add(son3)
db.session.add(son4)
db.session.add(son5)
db.session.add(user0)
db.session.add(user1)
db.session.add(user2)
db.session.commit()
son_query = Son.query.filter(Son.name.ilike('%a%'))
son_query_all = son_query.all()
print son_query.all()
user_query = User.query.filter(User.son.contains(son_query_all[0])).union(*[User.query.filter(User.son.contains(query)) for query in son_query_all[1:]])
print user_query.all()
The example firstly creates two models: User and Son, and then creates 3 User instances and 5 Son instances. user0 contains son1 and son2, user1 contains son3 and son4, and user2 contains son5. Note that the name of son1 and son4 are both like %a%. Now I want to select all User instances containing Son instances whose name likes %a%.
The current method is to select all Son instances in son_query_all, and then selects User instances containing individual desired Son instances, and then combines the selecting result using union. Is there a more SQL-style way for SQLAlchemy to select the same? For example, is there anything like contains_any so that the last query can be changed into something like
user_query = User.query.filter(User.son.contains_any(son_query_all))
Note that of course I can define a custom contains_any function for the same purpose using the union and contains operation. My question is whether there is a more efficient way than simply union all contains-ed?
The correct way to solve this kind of filtering is to use JOIN. Basically you join Son table with User table and filter by joined entity's field.
In SQLAlchemy you can express it like this:
User.query.join(Son).filter(Son.name.ilike('%a%'))
And it will produce following SQL:
SELECT * FROM user
JOIN son ON user.id = son.user_id
WHERE lower(son.name) LIKE lower('%a%')

Reducing queries for manytomany models in django

EDIT:
It turns out the real question is - how do I get select_related to follow the m2m relationships I have defined? Those are the ones that are taxing my system. Any ideas?
I have two classes for my django app. The first (Item class) describes an item along with some functions that return information about the item. The second class (Itemlist class) takes a list of these items and then does some processing on them to return different values. The problem I'm having is that returning a list of items from Itemlist is taking a ton of queries, and I'm not sure where they're coming from.
class Item(models.Model):
# for archiving purposes
archive_id = models.IntegerField()
users = models.ManyToManyField(User, through='User_item_rel',
related_name='users_set')
# for many to one relationship (tags)
tag = models.ForeignKey(Tag)
sub_tag = models.CharField(default='',max_length=40)
name = models.CharField(max_length=40)
purch_date = models.DateField(default=datetime.datetime.now())
date_edited = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
buyer = models.ManyToManyField(User, through='Buyer_item_rel',
related_name='buyers_set')
comments = models.CharField(default='',max_length=400)
house_id = models.IntegerField()
class Meta:
ordering = ['-purch_date']
def shortDisplayBuyers(self):
if len(self.buyer_item_rel_set.all()) != 1:
return "multiple buyers"
else:
return self.buyer_item_rel_set.all()[0].buyer.name
def listBuyers(self):
return self.buyer_item_rel_set.all()
def listUsers(self):
return self.user_item_rel_set.all()
def tag_name(self):
return self.tag
def sub_tag_name(self):
return self.sub_tag
def __unicode__(self):
return self.name
and the second class:
class Item_list:
def __init__(self, list = None, house_id = None, user_id = None,
archive_id = None, houseMode = 0):
self.list = list
self.house_id = house_id
self.uid = int(user_id)
self.archive_id = archive_id
self.gen_balancing_transactions()
self.houseMode = houseMode
def ret_list(self):
return self.list
So after I construct Itemlist with a large list of items, Itemlist.ret_list() takes up to 800 queries for 25 items. What can I do to fix this?
Try using select_related
As per a question I asked here
Dan is right in telling you to use select_related.
select_related can be read about here.
What it does is return in the same query data for the main object in your queryset and the model or fields specified in the select_related clause.
So, instead of a query like:
select * from item
followed by several queries like this every time you access one of the item_list objects:
select * from item_list where item_id = <one of the items for the query above>
the ORM will generate a query like:
select item.*, item_list.*
from item a join item_list b
where item a.id = b.item_id
In other words: it will hit the database once for all the data.
You probably want to use prefetch_related
Works similarly to select_related, but can deal with relations selected_related cannot. The join happens in python, but I've found it to be more efficient for this kind of work than the large # of queries.
Related reading on the subject