I'm stuck on a seemingly simple issue. I want to do a different queryset if sold_data is empty. Is there an effective way to do this? Or do I need to use a for loop and loop over all the listing objects and check each one?
class Listing(models.Model):
list_price = models.IntegerField()
sold_price = models.IntegerField(null=True, blank=True)
... other fields
data = Listing.objects.filter(...) # Note: I had already made other queries
if sold_price == None:
data = data.filter(list_price__gte=1)
else:
data = data.filter(sold_price__gte=1)
You can do it using Q object.
from django.db.models import Q
# your filtered queryset is in 'data' varibale
data.filter(Q(sold_price__isnull=False, sold_price__gte=1) | Q(sold_price__isnull=True, list_price__gte=1))
if you wanna check if an object is None use the is operator
I'm not sure if I did understand your question here is what I get : you wanna filter list_price if the data contains an object with empty value else filter sold_price
You can try this
if data.filter(sold_price__isnull=True).exists():
data = data.filter(list_price__gte=1)
else:
data = data.filter(sold_price__gte=1)
Related
I have the following models:
## Tags for issues
class issueTags(models.Model):
name = models.CharField(max_length=400)
class issues(models.Model):
tags = models.ManyToManyField(issueTags,blank = True)
In my view I get an array from some client side JavaScript i.e.
(Pdb) array_data = request.POST['arr']
(Pdb) array_data
'["2","3"]'
How should I filter my issues object to find all issues which match all tags in the array? (the 2,3 are the ID values for tag__id.
If there is a better way to arrange the objects that would also work so I can search in this fashion.
At the time of writing this, the existing answers are either incorrect (e.g. filtering matching all Issues that have any of the specified tags and the correct tag count) or inefficient (e.g. attaching filters in a loop).
For the following models:
class IssueTag(models.Model):
name = models.CharField(max_length=400, blank=True)
class Issue(models.Model):
label = models.CharField(max_length=50, blank=True)
tags = models.ManyToManyField(IssueTag, related_name='issues')
I suggest using Django Annotation in conjunction with a filter like so:
from django.db.models import Count, Q
tags_to_match = ['tag1', 'tag2']
issues_containing_all_tags = Issue.objects \
.annotate(num_correct_tags=Count('tags',
filter=Q(tags__name__in=tags_to_match))) \
.filter(num_correct_tags=2)
to get all Issues that have all required tags (but may have additional tags, as is required in the question).
This will produce the following SQL query, that resolves all tag matching in a single IN clause:
SELECT "my_app_issue"."id", "my_app_issue"."label",
COUNT("my_app_issue_tags"."issuetag_id")
FILTER (WHERE "my_app_issuetag"."name" IN ('tag1', 'tag2'))
AS "num_correct_tags"
FROM "my_app_issue"
LEFT OUTER JOIN "my_app_issue_tags" ON ("my_app_issue"."id" = "my_app_issue_tags"."issue_id")
LEFT OUTER JOIN "my_app_issuetag" ON ("my_app_issue_tags"."issuetag_id" = "my_app_issuetag"."id")
GROUP BY "my_app_issue"."id", "my_app_issue"."label"
HAVING COUNT("my_app_issue_tags"."issuetag_id")
FILTER (WHERE ("my_app_issuetag"."name" IN ('tag1', 'tag2'))) = 2;
args=('tag1', 'tag2', 'tag1', 'tag2', 2)
I haven't tested this, but I think you could do the following:
from django.db.models import Q
array_data = array_data.split(',')
issues.objects.filter(
tags__in=array_data,
).exclude(
# Exclude any that aren't in array_data
~Q(tags__in=array_data)
).annotate(
matches=Count(tags, distinct=True)
).filter(
# Make sure the number found is right.
matches=len(array_data)
)
FYI, you should be using Issue, IssueTag for your model names to follow Django's naming pattern.
It isn't most elegant solution or pythonic but I ended up just looping around the resulting filter.
def filter_on_category(issue_object,array_of_tags):
#keep filtering to make an and
i = 0
current_filter = issue_object
while (i < (len(array_of_tags))):
#lets filter again
current_filter=current_filter.filter(tags__id__in=array_of_tags[i])
i=i+1
return current_filter
Django field lookups argument (__) for many-to-many fields needs list argument. I have created a dummy list for each array element of IssueTags and pass it to lookups argument and it works as expected.
Let you have this models:
class IssueTags(models.Model):
name = models.CharField(max_length=400)
class Issues(models.Model):
tags = models.ManyToManyField(IssueTags,blank = True)
You want to get Issues which contains all of these IssueTags = ["1","2","3"]
issue_tags_array = ["1","2","3"]
#First initialize queryset
queryset = Issues.objects.all()
i = 0
while i < len(issue_tags_array):
#dummy issue_tag list
issue_tag = [issue_tags_array[i]]
#lets filter again
queryset = queryset.filter(tags__id__in=issue_tag)
i=i+1
return queryset
I apologize in advance if my question has already been there, but I have not found.
there is a model:
class Artikul_cabinets(models.Model):
artikul_cabinets = models.CharField(verbose_name="Артикул шкафа", max_length=20)
title_cabinets = models.CharField(verbose_name="Описание шкафа", max_length=200)
width_cabinets = models.ManyToManyField(Width_cabinets)
depth_cabinets = models.ManyToManyField(Depth_cabinets)
unit_cabinets = models.ManyToManyField(Unit_cabinets)
weight_cabinets = models.ManyToManyField(Weight_cabinets)
type_cabinets = models.ForeignKey(Type_cabinets, default=1)
color_cabinets = models.ForeignKey(Color_cabinets)
glass_cabinets = models.ManyToManyField(Glass_cabinets)
class Meta:
verbose_name_plural = "Артикул шкафа"
def __str__(self):
return self.artikul_cabinets
It is necessary to make the selection on the field
glass_cabinets = models.ManyToManyField(Glass_cabinets)
The selection is done as follows
data = Artikul_cabinets.objects.filter(Q(glass_cabinets=perf) &
Q(glass_cabinets=glass)
perf and glass the variables with different values.
And I returned to my empty QuerySet, although the database element with the parameters 'perf' and 'glass' are present in the record.
Tell me what I'm doing wrong.
also tried:
data = Artikul_cabinets.objects.filter(Q(glass_cabinets=perf),
Q(glass_cabinets=glass)
and also did not work, though if you put the operator '|' the conditions or work out correctly.
So I think you should do Artikul_cabinets.objects.filter(glass_cabinets=perf).filter(glass_cabinets=glass)
check How to filter model results for multiple values for a many to many field in django
I want the user to be able to order a list of objects in a table using javascript. Then, in a django function I would like to sort those object based on the same ordering, not on an attribute.
Is it possible? I was thinking about passing a list of pk from the template to the view and then ordering the objects according to this list, but I have not found a way to do it yet.
I don't think this is possible with queryset. Try following:
pk_list = [2, 1, 3, 4]
pk2obj = {obj.pk: obj for obj in Model.objects.all()}
objects_ordered = [pk2obj[pk] for pk in pk_list]
pkg2obj is mapping between pk and model instance object. To make a dictionary I used dictionary comprehension.
If you want to omit deleted objects:
objects_ordered = [pk2obj[pk] for pk in pk_list if pk in pk2obj]
Else if you want to replace deleted objects with default value (None in following code):
objects_ordered = [pk2obj.get(pk, None) for pk in pk_list]
I've had to solve this exact problem before.
If you want the user to be able to reorder them into a user-defined order, you can easily define a field to store this order.
As you say, initially, you could serve them in order according to id or an upload_date DateTimeField. But you could also have an PositiveIntegerField in the model, named position or order, to represent the user-defined order.
class MediaItem(models.Model):
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add = True)
position = models.PositiveIntegerField()
Whenever a user changes the order on the frontend, the JS can send the new order as an array of objects (ie. new_order = [{"pk":3, "position":1}, {"pk":1, "position":2}, {"pk":2, "position":3}]). The view can look up each instance by pk, and change the position:
for obj in new_order:
media_item = MediaItem.objects.get(pk=obj['pk'])
media_item.position = obj['position']
media_item.save()
Then always query using
objects_ordered.objects.order_by('position')
That's how we managed to do it. If you have more specific questions regarding this approach, feel free to ask in the comments.
Edit:
If the same object can be a member of many different groups or lists, and you want to store the position of the membership within that list, you can achieve this using a through model. A through model is useful when you need to store data that relates to the relationship between two objects that are related. In addition to the MediaItem class shown above, this is what your other models would look like:
class Album(models.Model):
media_items = models.ManyToManyField(MediaItem,
related_name = 'album_media_items',
through = 'Membership')
class Membership(models.Model):
album = models.ForeignKey(Album,
related_name = 'album')
media_item = models.ForeignKey(MediaItem,
related_name = 'media_item')
date = models.DateTimeField(auto_now_add = True)
position = models.PositiveIntegerField()
Then, you could query the Membership instances, instead of the MediaItem instances.
# get id of list, or album...
alb = Album.objects.get(pk=id_of_album)
media_items = Membership.objects.filter(album=alb).order_by('position')
for item in media_items:
# return the items, or do whatever...
# keep in mind they are now in the user-defined order
You can do this:
pk_list = [1,5,3,9]
foo = Foo.objects.filter(id__in=pk_list)
#Order your QuerySet in base of your pk_list using Lambda
order_foo = sorted(foo, key = lambda:x , pk_list.index(x.pk))
I'm trying to write an internal API in my application without necessarily coupling it with the database.
class Product(models.Model):
name=models.CharField(max_length=4000)
price=models.IntegerField(default=-1)
currency=models.CharField(max_length=3, default='INR')
class Image(models.Model):
# NOTE -- Have changed the table name to products_images
width=models.IntegerField(default=-1)
height=models.IntegerField(default=-1)
url=models.URLField(max_length=1000, verify_exists=False)
product=models.ForeignKey(Product)
def create_product:
p=Product()
i=Image(height=100, widght=100, url='http://something/something')
p.image_set.add(i)
return p
Now, when I call create_product() Django throws up an error:
IntegrityError: products_images.product_id may not be NULL
However, if I call p.save() & i.save() before calling p.image_set.add(i) it works. Is there any way that I can add objects to a related object set without saving both to the DB first?
def create_product():
product_obj = Product.objects.create(name='Foobar')
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something', product=product_obj)
return product_obj
Explanation:
Product object has to be created first and then assign it to the Image object because id and name here is required field.
I am wondering why wouldn't you not require to make a product entry in DB in first case? If there is any specific reason then i may suggest you some work around?
EDIT: Okay! i think i got you, you don't want to assign a product to an image object initially. How about creating a product field as null is equal to true.
product = models.ForeignKey(Product, null=True)
Now, your function becomes something like this:
def create_product():
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something')
return image_obj
Hope it helps you?
I got same issue with #Saurabh Nanda
I am using Django 1.4.2. When I read in django, i see that
# file django/db/models/fields/related.py
def get_query_set(self):
try:
return self.instance._prefetched_objects_cache[rel_field.related_query_name()]
except (AttributeError, KeyError):
db = self._db or router.db_for_read(self.model, instance=self.instance)
return super(RelatedManager,self).get_query_set().using(db).filter(**self.core_filters)
# file django/db/models/query.py
qs = getattr(obj, attname).all()
qs._result_cache = vals
# We don't want the individual qs doing prefetch_related now, since we
# have merged this into the current work.
qs._prefetch_done = True
obj._prefetched_objects_cache[cache_name] = qs
That 's make sese, we only need to set property _prefetched_objects_cache for the object.
p = Product()
image_cached = []
for i in xrange(100):
image=Image(height=100, widght=100, url='http://something/something')
image_cached.append(image)
qs = p.images.all()
qs._result_cache = image_cached
qs._prefetch_done = True
p._prefetched_objects_cache = {'images': qs}
Your problem is that the id isn't set by django, but by the database (it's represented in the database by an auto-incremented field), so until it's saved there's no id. More about this in the documentation.
I can think of three possible solutions:
Set a different field of your Image model as the primary key (documented here).
Set a different field of your Production model as the foreign key (documented here).
Use django's database transactions API (documented here).
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