I have a blog model with m2m relationship with a tag object.I want each blog to have multiple sets of tags say [tagset1,tagset2......]. When user submits some sets of tags[usertagset1,usertagset2.....] to retrieve blogs, I have to return all blogs with
usertagset being subset of blog's tagset.
I am not sure of best way to go about it with minimum no. of sql queries.
Should I create a new model that holds the info of blog's tagsets and then have the blog model an m2m relation with this new model?
You can try to filter out tags one by one:
user_tags = [tag1, tag2, ... tagn]
blogs = Blogs.objects.all()
for tag in user_tags:
blogs = blogs.filter(tags=tag) # assuming 'tags' is your M2M field name
Each filter() will narrow your search until only blogs having all specified tags are remaining. Since Django's QuerySets are lazy and can be combined, this will result in a single SQL query.
Related
I have these models:
class Notebook(models.Model):
title = models.CharField(max_length=200)
class Tag(models.Model):
name = models.CharField(max_length=63)
notebooks = models.ManyToManyField(Notebook, related_name='tags')
and I'm trying to find all Notebooks that have two particular tags (I can probably deduce the rest of the query if I can get two tags working)
I'm defining a query for the two tags with:
query = Q(name__iexact='dna') | Q(name__iexact='notebook')
and I can filter the pertinent tags with:
Tag.objects.filter(query)
But I'm seeking all the Notebooks that have these tags. In SQL I would do a JOIN but the ORM method select_related apparently doesn't work with ManyToManyField
Under spanning multi-valued relationships, the documentation says:
Successive filter() calls further restrict the set of objects, but for multi-valued relations, they apply to any object linked to the primary model, not necessarily those objects that were selected by an earlier filter() call.
The examples following then show filter being chained when querying a m2m relationship. So based on the models listed in the question, you can create a query and then loop through the tags to keep adding a .filter(tags_name__iexact=tag) to the query object
tags = ['dna', 'notebook']
q = Notebook.objects
for tag in tags:
q = q.filter(tags__name__iexact=tag)
return q
I initially thought the approach would involve constructing a big Q object with a reduce lamba but chaining works.
You can filter using the related_name:
Notebook.objects.filter(Q(tags__name__iexact='dna') | Q(tags__name__iexact='notebook'))
OR
tags = ['dna', 'notebook']
Notebook.objects.filter(tags__name__in=tags)
I have Article model with title and body fields. I'm building a search functionality and need to filter for articles that have keywords either in title or body fields.
I have two Articles. One has "candy" in the title and the other has "candy" in the body. So my result filter should have both articles. I'm trying below query but it's bringing me the first article only
Article.objects.filter(title__icontains='candy').filter(body__icontains='candy')
Thx
You need to use Q objects.
Article.objects.filter(Q(title__icontains='candy')|Q(body__icontains='candy'))
Django 1.7
I have a model:
class Model(models.Model):
tags = models.ManyToManyField(..)
When I do Model.objects.prefetch_related().... it results in many individual queries being issued to fetch the tags, one for each model.
I'd expect 2 queries to happen: 1 to fetch the models, another to fetch the tags for all models.
How to do that?
EDITED:
I'm using a raw query like Model.objects.prefetch_related('tags').raw_query(..)
You should specify a field name to prefetch:
Model.objects.prefetch_related('tags')
If you use the queryset.raw() method then prefetch_related() logic doesn't work.
I have been mulling over this for a while looking at many stackoverflow questions and going through aggregation docs
I'm needing to get a dataset of PropertyImpressions grouped by date. Here is the PropertyImpression model:
#models.py
class PropertyImpression(models.Model):
'''
Impression data for Property Items
'''
property = models.ForeignKey(Property, db_index=True)
imp_date = models.DateField(auto_now_add=True)
I have tried so many variations of the view code, but I'm posting this code because I consider to be the most logical, simple code, which according to documentation and examples should do what I'm trying to do.
#views.py
def admin_home(request):
'''
this is the home dashboard for admins, which currently just means staff.
Other users that try to access this page will be redirected to login.
'''
prop_imps = PropertyImpression.objects.values('imp_date').annotate(count=Count('id'))
return render(request, 'reportcontent/admin_home.html', {'prop_imps':prop_imps})
Then in the template when using the {{ prop_imps }} variable, it gives me a list of the PropertyImpressions, but are grouped by both imp_date and property. I need this to only group by imp_date, and by adding the .values('imp_date') according to values docs it would just be grouping by that field?
When leaving off the .annotate in the prop_imps variable, it gives me a list of all the imp_dates, which is really close, but when I group by the date field it for some reason groups by both imp_date and property.
Maybe you have defined a default ordering in your PropertyImpression model?
In this case, you should add order_by() before annotate to reset it :
prop_imps = PropertyImpression.objects.values('imp_date').order_by() \
.annotate(count=Count('id'))
It's explained in Django documentation here:
Fields that are mentioned in the order_by() part of a queryset (or which are used in the default ordering on a model) are used when selecting the output data, even if they are not otherwise specified in the values() call. These extra fields are used to group “like” results together and they can make otherwise identical result rows appear to be separate. This shows up, particularly, when counting things.
I'm trying to optimise my queries but prefetch_related insists on joining the tables and selecting all the fields even though I only need the list of ids from the relations table.
You can ignore the 4th query. It's not related to the question.
Related Code:
class Contact(models.Model):
...
Groups = models.ManyToManyField(ContactGroup, related_name='contacts')
...
queryset = Contact.objects.all().prefetch_related('Groups')
Django 1.7 added Prefetch objects which let you customise the queryset used when prefetching.
In particular, see only().
In this case, you'd want something like:
queryset = Contact.objects.all().prefetch_related(
Prefetch('Groups', queryset=Group.objects.all().only('id')))