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
Related
I have the following code.
queryset = Registry.objects.filter(
epas_id__contains=uuid_obj_id
).values_list('pk', flat=True)
Based on my data, this correctly returns the following:
<QuerySet [UUID('d9a0977c-5bc0-4667-af24-5e95b83761d4'), UUID('b2d0f086-0a55-44cc-b1ba-3ebf598d24ae')]>
But what I want to do is extract just the values of pk in a list, so something like this:
['d9a0977c-5bc0-4667-af24-5e95b83761d4', 'b2d0f086-0a55-44cc-b1ba-3ebf598d24ae']
Any thoughts and help on how I can achieve this would be appreciated.
We can work with list comprehension, and call str(…) on each element, like:
queryset = Registry.objects.filter(
epas_id__contains=uuid_obj_id
).values_list('pk', flat=True)
result = [str(pk) for pk in queryset]
I have a list of objects and a model includes ManyToManyField of this object.
I'd like to get objects who have the same list of objects in this field.
that's means __in won't work because he will do OR between the objects in the list and not AND.
I was trying using the AND Q Lookup, but it didn't work (after checking the .query text, it seems that he doing the AND inside the field object, instead of the object itself, so obviously the field object id don't have two different ids..)
here's the interceptor I played with
results_query_set[0]
<Results: Results object (2)>
results_query_set[0].users.all()
<QuerySet [<User: test_user>, <User: test_user_2>]>
users
[<User: test_user>, <User: test_user_2>]
users_q
<Q: (AND: ('users', <User: test_user>), ('users', <User: test_user_2>))>
results_query_set.filter(users=users[0])
<QuerySet [<Results: Results object (2)>]>
results_query_set.filter(users=users[1])
<QuerySet [<Results: Results object (2)>]>
results_query_set.filter(users_q)
<QuerySet []>
results_query_set.filter(Q(users=users[0]) & Q(users=users[1]))
<QuerySet []>
and the result results_query_set.filter(users_q).query.__str__() reproduce is
'SELECT "results_table"."id", "results_table"."date", "results_table"."lookup", "results_table"."value" FROM "results_table" INNER JOIN "results_table_users" ON ("results_table"."id" = "results_table_users"."widgetresults_id") WHERE ("results_table_users"."user_id" = 1 AND "results_table_users"."user_id" = 2)
I can chain .filter for each user, but of course, I'd like to make one query instead of queries by the numbers of my input.
You need to JOIN the target table (User in your case) multiple times (for every single user) in order to build such a query.
The way to do this in Django is by calling .filter multiple times.
users = [user1, user2] # a list of users your are interested to filter on
initial_qs = Results.objects.all() # or whatever your results_query_set is
result_qs = reduce(lambda qs, user: qs.filter(users=user.id), users, initial_qs)
# At this point you will have results containing user1 and user2
# but this also includes results with more users (e.g. users 1, 2 and 3)
# if you want to exclude those, you need to filter by the total users count too
result_qs = result_qs.annotate(cnt=models.Count('users')).filter(cnt=len(users))
I have not tried this, but I think you might be able to use postgresql's array_agg function. The Django implementation is here.
from django.contrib.postgres.aggregates import ArrayAgg
ideal_user_list = [] # some list.
# Or fetch it directly from the db using the below query and `get`
Results.objects.annotate(
related_user_array=ArrayAgg('users__id', ordering='users__id')
).filter(related_user_array=ideal_user_list)
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
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'
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)