Currently I am practicing for annotate and have some confusion regarding below code.
>>> b = Book.objects.all().annotate(upper_name = Upper('name'))
>>> b[0].name
'Book1'
>>> b[0].upper_name
'BOOK1'
>>> ba = Book.objects.annotate(upper_name = Upper('name'))
>>> ba[0]
<Book: Book1>
>>> ba[0].name
'Book1'
>>> ba[0].upper_name
'BOOK1'
I am getting same output when not using all() so what is difference between using Book.objects.all() and 'Book.objects.annotate()'.
How doing annotate() on Book objects without all() provide all Book objects.
I have read Django documentation but not able to find any answer.
Thanks.
There is no difference because all actually calls get_queryset on model manager to return queryset. You can check the implementation of BaseManager to see this.
Using all() is preferable because it's guaranteed to return a QuerySet instance that you can further iterate/filter/etc, where using manager returns Manager instance that you can filter/annotate/whatever but can't use in same way as queryset.
Example:
for book in Book.objects:
# this will fail
for book in Book.objects.all():
# this will work
Related
I want to check if given field is SELECTed in given queryset. Assuming there is Queryset.has() method, this should run without AssertionError:
qs = Author.objects.all()
assert not qs.has('articles_count')
qs = qs.annotate(articles_count=Count('articles'))
assert qs.has('articles_count')
qs = qs.values('pk')
assert not qs.has('articles_count')
(assume model Author has no field articles_count by default)
I am late to the party, but wanted to share my solution if anyone needs it in the future :)
Columns are not present on the QuerySet, but you can check annotated columns on the Query:
authors = Author.objects.all().annotate(articles_count=Count('articles'))
assert 'articles_count' in authors.query.annotations
Or you could just get one author from QuerySet and check if it has required field:
author = Author.objects.annotate(articles_count=Count('articles')).first()
assert hasattr(author, 'articles_count')
I would prefer the second method since it's not diving deep into Django's internal system.
If you are planning to use complete QuerySet, but don't want to execute two queries you could evaluate QuerySet and then slice it like so:
authors = Author.objects.annotate(articles_count=Count('articles'))
bool(authors) # Just here to evaluate the queryset
assert hasattr(authors[0], 'articles_count') # Doesn't query the database again
I had a similar issue and used the try - except logic :
try:
queryset.columnname
except:
print("Nope, columnname is not in queryset")
Hope it helps somebody.
Cheers
You can try Python's hasattr method:
In [3]: hasattr(qs, "articles_count")
Out[3]: False
Or if you want to check this attr of every model instance:
all([hasattr(x, "articles_count") for x in qs])
I have model form with several fields works as expected. Now I need, for specific reasons, to get form field in view but got error 'EditPostForm' object has no attribute 'about' when I call mydata1 = form.about in view. But about field exist of course. form.data.about also wont work etc. So how can I get it? Thanks.
If you form has instance associated to it, you can try
post = EditPost.objects.get(id=id)
form1 = EditPostForm(instance=post)
form1.instance.about
Based on your comment below if you are using ManyToMany relation you can get the value as
>>> bf = BookForm(instance=book)
>>> bf.instance.authors
<django.db.models.fields.related.ManyRelatedManager object at 0x0000000004658B38>
>>> bf.instance.authors.all() #which returns a query set of related objects
[<Author: Kotian>]
>>> bf.instance.authors.all()[0]
<Author: Kotian>
>>> bf.instance.authors.all()[0].name
u'Kotian'
or based on how you have defined the ManyToMany
>>> af = AuthorForm(instance=author)
>>> af.instance.name
u'MyName'
>>> af.instance.book_set
<django.db.models.fields.related.ManyRelatedManager object at 0x0000000004658C18>
>>> af.instance.book_set.all() # returns queryset
[<Book: Book object>, <Book: Book object>]
>>> af.instance.book_set.all()[0] #accessing first object here
<Book: Book object>
>>> af.instance.book_set.all()[0].name
u'Lepord'
in a view I am constructing I need to consult multiple databases. What I want to do is use the results of on query_set to search another db table.
I have functioning mydb1_query_set, what I need now is something like this:
for row in mydb1_query_set:
mydb2_query_set = Mytable.objects.filter(id=row.id)
So that I keep adding to the initially empty mydb2_query_set as I iterate. I realize that there is no QuerySet.append, so how do I achieve what I want? Any help much appreciated...
Use a list instead of a queryset, and then you can append or extend as you wish.
mydb2_query = []
for row in mydb1_query_set:
mydb2_query.extend(list(Mytable.objects.filter(id=row.id)))
qs1= <QuerySet [<User: 1#gmail.com>, <User: 2#gmail.com>]>
qs2= <QuerySet [<User: 3#gmail.com>, <User: 4#gmail.com>]>
qs3 = qs1 | qs2
res:
qs3 = <QuerySet [<User: 1#gmail.com>, <User: 2#gmail.com>, <User:3#gmail.com>, <User: 4#gmail.com>]>
EDIT: Link to documentation:
https://docs.djangoproject.com/en/2.1/ref/models/querysets/#operators-that-return-new-querysets
Using python's built in itertools module worked best for me.
from itertools import chain
qs1 = Mytable.objects.filter(id=2)
qs2 = Mytable.objects.filter(id=3)
all_queries = chain(qs1, qs2)
Here's the docs, in case you'd like some reference materials: https://docs.python.org/3/library/itertools.html#itertools.chain
Django's Managers object provide a default method called .get_queryset() which allow you to concatenate extra queries after it:
queryset = MyModel.objects.get_queryset()
if username:
queryset = queryset.filter(username=username)
if country:
queryset = queryset.filter(country=country)
You could easily using .query attribute getting the SQL, for example:
>>> print(queryset.query)
SELECT `myapp_mymodel`.`id`, `myapp_mymodel`.`username`, `myapp_mymodel`.`country` FROM `myapp_mymodel` WHERE `myapp_mymodel`.`country` = foo
I have a third-party function which gives me a filtered queryset (e.g. records with 'valid'=True) but I want to remove a particular condition (e.g. to have all records, both valid and invalid).
Is there a way to remove a filter condition to an already-filtered queryset?
E.g.
only_valid = MyModel.objects.filter(valid=True)
all_records = only_valid.**remove_filter**('valid')
(I know that it would be better to define 'all_records' before 'only_valid', but this is just an example...)
Although there is no official way to do this using filter notation, you may easily do it with Q-notation.
For example, if you ensure that third-part function returns a Q object, not a filtered QuerySet, you may do the following:
q = ThirdParty()
q = q | Q(valid=False)
And the resulting SQL conditions will be joined using OR operator.
From the docs:
Each time you refine a QuerySet, you get a brand-new QuerySet that is in no way bound to the previous QuerySet. Each refinement creates a separate and distinct QuerySet that can be stored, used and reused.
I doubt therefore, that there is a standard way to do it. You could dig into the code, see, what filter() does and try a bit. If that doesn't help, my assumption is, you're out of luck and need to re-build the query yourself.
Use this function
from django.db.models import Q
def remove_filter(lookup, queryset):
"""
Remove filter lookup in queryset
```
>>> queryset = User.objects.filter(email='user#gmail.com')
>>> queryset.count()
1
>>> remove_filter('email', queryset)
>>> queryset.count()
1000
```
"""
query = queryset.query
q = Q(**{lookup: None})
clause, _ = query._add_q(q, self.used_aliases)
def filter_lookups(child):
return child.lhs.target != clause.children[0].lhs.target
query.where.children = list(filter(filter_lookups, query.where.children))
Here's what I did in a similar case.
all_records = MyModel.objects.all()
only_valid = MyModel.objects.filter(valid=True)
only_valid.original = all_records
...
all_records = only_valid.original
Obviously this clears any other filters too so it won't be right for every case.
original_query_set = MyModel.objects.filter(**conditions)
model_class = orginal_query_set.model
new_query_set = model_class.objects.filter(**new_conditions)
You can use the .model attribute on a QuerySet to get the model class, then use the model class to make a brand new QuerySet.
Thanks for making me check the source code Boldewyn. So, it seems there's a new method right below filter() with the same parameters... called exclude() (as of commit mini-hash ef6c680, for when it loses its line number)
Return a new QuerySet instance with NOT (args) ANDed to the existing set.
So, to answer the original question:
only_valid = MyModel.objects.filter(valid=True)
filtered_results = only_valid.exclude(the_condition_to_remove=True)
If you have some models:
class Teacher(models.Model):
name = models.CharField(max_length=50)
class Student(models.Model):
age = models.PositiveIntegerField()
teacher = models.ForeignKey(Teacher, related_name='students')
and you use it like this:
>>> student = Student.objects.get(pk=1)
>>> student.teacher.name # This hits the database
'Some Teacher'
>>> student.teacher.name # This doesn't (``teacher`` is cached on the object)
'Some Teacher'
That's awesome. Django caches the related object so that you can use it again without having to abuse your database.
But, if you use it like this:
>>> teacher = Teacher.objects.get(pk=1)
>>> for student in teacher.students.all(): # This hits the database
... print(student.age)
...
8
6
>>> for student in teacher.students.all(): # This does too (obviously)
... print(student.age)
...
8
6
There's no caching or efficient access to related objects this direction.
My question is thus: Is there a built-in (or non-problematic way) to backward access related objects in an efficient way (a cached way), like you can in the student.teacher example above?
The reason I want this is because I have a model with multiple methods that need access to the same related objects over and over, so a page that should have 12 queries ends up with about 30.
There isn't any built-in way. I've written about this issue on my blog, with a technique to optimise accessing reverse relationships.
orokusaki,
You just need to cache the queryset as a python variable
students = teacher.students.all()
And then just use students in your for loops.
Below is a link to Django's own documentation about this specific issue :-)
http://docs.djangoproject.com/en/1.1/topics/db/optimization/#understand-cached-attributes
Try this perhaps?
teacher = Teacher.objects.select_related().get(pk=1)
http://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.QuerySet.select_related
I have never used select_related in concert with a .all() on its result, so I'm not sure if it will yield DB savings or not.