Searching via multiple form fields? - django

I'm displaying a page with a 4 textboxes, each storing the user entered value into a separate variable.
My Database has 4 columns, each corresponding to one text box's variable.
I want to do this:
The user can enter values in 1,2,3 or all 4 of the textboxes, and I want to trim the search results according to each value that they've entered. Each textbox is optional.
How do I do this? I'm new at Django, and I only know how to search using one search term.

I think you need Q objects. See: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects
From the docs:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
Translates to:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
You can populate the Q query from your cleaned form - you might even want to override clean() and return the queryset that way, so that you could just access the query set from the validated form.

Related

Django: how to query all values in an array?

I have a checkbox with multiple values. For example, hobbies. When the user choose some hobbies, he wants to see the person who has all those hobbies.
I dont go into details for sake of clarity so here's how I read all the checked values and put them in my "big" query, which is, as you may guess: q:
hobbies2 = [int(l) for l in g.getlist('hobbies2')]
if len(hobbies2):
q = q & Q(personne__hobbies2__in=hobbies2)
The problem is that it returns all persons who have a hobby in common (i.e. it's like "or", not "and").
How to get all those many to many values with a and?
The __in operator is for filtering by a set. It is not meant for checking multiple values at once in a many-to-many relationship.
It literally translate to the following in SQL:
SELECT ... WHERE hobbies IN (1, 3, 4);
So you'd need to build your query using AND conditions. You can do this with consecutive filters:
queryset = Personne.objects.all()
# build up the queryset with multiple filters
for hobby in hobbies2:
queryset = queryset.filter(hobbies2=hobby)

django AND on Q not working for many to many

Here's the model:
class ModelA:
title = charfield
m2m = foreignkey, relatedname='m2ms'
This is working:
ModelA.objects.filter(Q(title__icontains='a') & Q(title__icontains='b'))
So it returns all records whose titles contains both letters 'a' and 'b'.
Then same not working for many to many:
ModelA.objects.filter(Q(m2ms__id=1) & Q(m2ms__id=2))
ModelA m2ms list:
for x in ModelA.objects.all():
print x.m2ms.all().values_list('id', Flat=True)
#Output:
1,2,3
1,2
1
1,3,5
4,6,7
1,8
So, expected output of ModelA.objects.filter(Q(m2ms__id=1) & Q(m2ms__id=2)) should be records having these m2m ids: [1,2,3], [1,2]. But its not happening. Why ?
I cant use Q(m2ms__id__in=[1,2]) because it returns same even if I do __in=[1,2,3,4, infinite numbers]
Reason for using Q instead of filter is mentioned in this question - django filter on many to many along with Q
Read this section of the docs.
Particularly this paragraph:
To handle both of these situations, Django has a consistent way of processing filter() and exclude() calls. Everything inside a single filter() call is applied simultaneously to filter out items matching all those requirements. 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.
I believe if you do ModelA.objects.filter(Q(m2ms__id__in=[1, 2])) or ModelA.objects.filter(m2ms__id__in=[1, 2]) it will work as you expect it to.

django pagination by giving textbox inputs

I'm a new pratikant and when i joined in the company they gave me the task of configuring number of items per page by giving textbox input for example i created a textbox in html and submmit button and now i want to insert some value say 2 then it should show me first 2 records of the items in the same way 3, 4, 5 ,.......now for me the problem is i was unable to make or attch or call my html code into views.py i mean i was able to make he static pagination
eg :
table.paginate(page=request.GET.get("page", 1), per_page = 10)
like this but now what i want is when i give input to the textbox,, PER_PAGE must be changed according to the given input like 2 records or 4 or n records
i donno whether it is easy or tough but im a starter of python programming and learning still i was unable to make it please help me
Fella student
Thanks in advance
Use
table.paginate(page=request.GET.get("page", 1), per_page = request.GET.get("per_page", 10))
That way 10 will be the default value if "per_page" wasn't passed.

How do I use django's Q with django taggit?

I have a Result object that is tagged with "one" and "two". When I try to query for objects tagged "one" and "two", I get nothing back:
q = Result.objects.filter(Q(tags__name="one") & Q(tags__name="two"))
print len(q)
# prints zero, was expecting 1
Why does it not work with Q? How can I make it work?
The way django-taggit implements tagging is essentially through a ManytoMany relationship. In such cases there is a separate table in the database that holds these relations. It is usually called a "through" or intermediate model as it connects the two models. In the case of django-taggit this is called TaggedItem. So you have the Result model which is your model and you have two models Tag and TaggedItem provided by django-taggit.
When you make a query such as Result.objects.filter(Q(tags__name="one")) it translates to looking up rows in the Result table that have a corresponding row in the TaggedItem table that has a corresponding row in the Tag table that has the name="one".
Trying to match for two tag names would translate to looking up up rows in the Result table that have a corresponding row in the TaggedItem table that has a corresponding row in the Tag table that has both name="one" AND name="two". You obviously never have that as you only have one value in a row, it's either "one" or "two".
These details are hidden away from you in the django-taggit implementation, but this is what happens whenever you have a ManytoMany relationship between objects.
To resolve this you can:
Option 1
Query tag after tag evaluating the results each time, as it is suggested in the answers from others. This might be okay for two tags, but will not be good when you need to look for objects that have 10 tags set on them. Here would be one way to do this that would result in two queries and get you the result:
# get the IDs of the Result objects tagged with "one"
query_1 = Result.objects.filter(tags__name="one").values('id')
# use this in a second query to filter the ID and look for the second tag.
results = Result.objects.filter(pk__in=query_1, tags__name="two")
You could achieve this with a single query so you only have one trip from the app to the database, which would look like this:
# create django subquery - this is not evaluated, but used to construct the final query
subquery = Result.objects.filter(pk=OuterRef('pk'), tags__name="one").values('id')
# perform a combined query using a subquery against the database
results = Result.objects.filter(Exists(subquery), tags__name="two")
This would only make one trip to the database. (Note: filtering on sub-queries requires django 3.0).
But you are still limited to two tags. If you need to check for 10 tags or more, the above is not really workable...
Option 2
Query the relationship table instead directly and aggregate the results in a way that give you the object IDs.
# django-taggit uses Content Types so we need to pick up the content type from cache
result_content_type = ContentType.objects.get_for_model(Result)
tag_names = ["one", "two"]
tagged_results = (
TaggedItem.objects.filter(tag__name__in=tag_names, content_type=result_content_type)
.values('object_id')
.annotate(occurence=Count('object_id'))
.filter(occurence=len(tag_names))
.values_list('object_id', flat=True)
)
TaggedItem is the hidden table in the django-taggit implementation that contains the relationships. The above will query that table and aggregate all the rows that refer either to the "one" or "two" tags, group the results by the ID of the objects and then pick those where the object ID had the number of tags you are looking for.
This is a single query and at the end gets you the IDs of all the objects that have been tagged with both tags. It is also the exact same query regardless if you need 2 tags or 200.
Please review this and let me know if anything needs clarification.
first of all, this three are same:
Result.objects.filter(tags__name="one", tags__name="two")
Result.objects.filter(Q(tags__name="one") & Q(tags__name="two"))
Result.objects.filter(tags__name_in=["one"]).filter(tags__name_in=["two"])
i think the name field is CharField and no record could be equal to "one" and "two" at same time.
in python code the query looks like this(always false, and why you are geting no result):
from random import choice
name = choice(["abtin", "shino"])
if name == "abtin" and name == "shino":
we use Q object for implement OR or complex queries
Into the example that works you do an end on two python objects (query sets). That gets applied to any record not necessarily to the same record that has one AND two as tag.
ps: Why do you use the in filter ?
q = Result.objects.filter(tags_name_in=["one"]).filter(tags_name_in=["two"])
add .distinct() to remove duplicates if expecting more than one unique object

Django form with multiple

I have a ModelForm class which can be used one or more times on a single page. E.g.:
class ProductForm(forms.ModelForm):
class Meta:
model = Product
exclude = ('prod_seq_number')
now when I want to use the form more than once on a single page, e.g.:
prodforms = []
for i in (range(nrofproducts)):
prodforms.append(ProductForm())
I can now pass the list prodforms to the template and the user can enter multiple products on the page. The variable nrofproducts is: 1, 2, 4, 8 or 16
This won't work because I will get form elements with identical names. I need to be able to differentiate between the various form elements. So I need a way to modify the form attributes
for instance by appending the index 'i' from the loop in the view code the 'name' attribute of the form. Any help would be appreciated.
The reason for making a form layout like this is that a user can choose to see 1, 2, 4, 8 or 16 products on a single page and I want a entry form to resemble the layout which he will see when finished.
You really don't want to do this. Simpler way of doing it would be to use Django Formset