Search multiple fields of django model without 3rd party app - django

I have a single model in my django app that I want to create a search form for. Is there a way to search all the fields in the model at once with the same search string? I've looked into xapian and solr but they seem like a lot of overhead for searching over 1 model. I want to be able to say something like:
results = Assignment.objects.filter(any_column = search_string)
I realize there might not be something that concise but right now the only option I can come up with other than using a search app is to check each field separately and concatenate the results together.

Once you have all the field names you can create Q objects using kwarg expansion and use reduce() along with operator.or_ to turn them into a single query.
qgroup = reduce(operator.or_, (Q(**{fieldname: value}) for fieldname in fieldnames))
asgns = Assignment.objects.filter(qgroup)

Old question, but for further reference I am adding this:
In django 1.10 SearchVector class was added.
Usage from the docs:
Searching against a single field is great but rather limiting. The Entry instances we’re searching belong to a Blog, which has a tagline field. To query against both fields, use a SearchVector:
>>> from django.contrib.postgres.search import SearchVector
>>> Entry.objects.annotate(
... search=SearchVector('body_text', 'blog__tagline'),
... ).filter(search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]

Related

override django oscar search app to return only merchant specific products?

I am using Django oscar(2.0.2), where I have made foreign key relation of merchant ids with product table AbstractProduct with data, (https://prnt.sc/s1ssxx) so that I can fetch merchant-specific products from the database. By default oscar returns all the products in the search results, I want to only return the products specific to a merchant's site.
I am using Haystack simple search as suggested in oscar documentation, I have tried overriding the search app like all other apps, I have overridden the search_indexes.py file, but it seems that it never gets called from the FacetedSearchView. I also tried to override the search handlers, but it was also not getting called.
I tried understanding oscar's search functionality, but on the shell, I get a warning,
UserWarning: The model is not registered for search.
warnings.warn('The model %r is not registered for search.' % (model,))
Model '' not handled by the routers. Model class oscar_apps.catalogue.models.Product not handled by the routers.
How can I register the Product model for search?
where will I have to override the query like that:
Product.objects.filter(user_id=1), to return only merchant-specific products while searching for a product?
I know, how to override apps, but could someone give an overview and explain to me the steps that will be required to override the search app, and get the basic sorting functionality working?
if my question is not clear, let me know in the comments so I can improve it.
#yajant, I'm not sure whether my answer might be useful to you, but I have faced the same issue and struggled to find out the solution. Hope this might help someone facing the same issue. Thanks
fork the search app and import oscar search and overwrite FacetedSearchView class get_results method as below
Override the get_results method in your forked search/views.py
def get_results(self):
# We're only interested in products (there might be other contenttypes
# in the Solr index).
qs = super().get_results().models(Product)
qs = qs.filter(user_id=1)
return qs

Should I use ArrayField or ManyToManyField for tags

I am trying to add tags to a model for a postgres db in django and I found two solutions:
using foreign keys:
class Post(models.Model):
tags = models.ManyToManyField('tags')
...
class Tag(models.Model):
name = models.CharField(max_length=140)
using array field:
from django.contrib.postgres.fields import ArrayField
class Post(models.Model):
tags = ArrayField(models.CharField(max_length=140))
...
assuming that I don't care about supporting other database-backends in my code, what is a recommended solution ?
If you use an Array field,
The size of each row in your DB is going to be a bit large thus Postgres is going to be using more toast tables
Every time you get the row, unless you specifically use defer the field or otherwise exclude it from the query via only, or values or something, you paying the cost of loading all those values every time you iterate across that row. If that's what you need then so be it.
Filtering based on values in that array, while possible isn't going to be as nice and the Django ORM doesn't make it as obvious as it does for M2M tables.
If you use M2M field,
You can filter more easily on those related values
Those fields are postponed by default, you can use prefetch_related if you need them and then get fancy if you want only a subset of those values loaded.
Total storage in the DB is going to be slightly higher with M2M because of keys, and extra id fields.
The cost of the joins in this case is completely negligible because of keys.
With that being said, the above answer doesn't belong to me. A while ago, I had stumbled upon this dilemma when I was learning Django. I had found the answer here in this question, Django Postgres ArrayField vs One-to-Many relationship.
Hope you get what you were looking for.
If you want the class tags to be monitored ( For eg : how many tags, how many of a particular tag etd ) , the go for the first option as you can add more fields to the model and will add richness to the app.
On the other hand, if you just want it to be a array list just for sake of displaying or minimal processing, go for that option.
But if you wish to save time and add richness to the app, you can use this
https://github.com/alex/django-taggit
It is as simple as this to initialise :
from django.db import models
from taggit.managers import TaggableManager
class Food(models.Model):
# ... fields here
tags = TaggableManager()
and can be used in the following way :
>>> apple = Food.objects.create(name="apple")
>>> apple.tags.add("red", "green", "delicious")
>>> apple.tags.all()
[<Tag: red>, <Tag: green>, <Tag: delicious>]

Use Haystack facets to provide multiple search fields

I am writing a simple library app (kind of just a book database at the moment) and I have a Haystack search page as the home page. I am trying to add multiple search fields to the page, so you can search specifically in the title, author etc. instead of just one generic "keyword" search. I have been trying to make this work with Haystack's "faceting" feature, but it seems to be more oriented around refining a search based on given, strict categories.
Can you use Haystack facets to provide multiple search fields? Or is Haystack just not cut out for this kind of job? If so, what should I be using instead?
If you need more context, the current project is available on GitHub.
Try ModelSearchForm. This form adds new fields to form. It iterates through all registered models for the current SearchSite and provides a checkbox for each one. If no models are selected, all types will show up in the results.
custom form example from documentation and can be converted to ModelSearchForm simply inherting from ModelSearcForm
from django import forms
from haystack.forms import SearchForm
class DateRangeSearchForm(SearchForm):
start_date = forms.DateField(required=False)
end_date = forms.DateField(required=False)
def search(self):
# First, store the SearchQuerySet received from other processing.
sqs = super(DateRangeSearchForm, self).search()
if not self.is_valid():
return self.no_query_found()
# Check to see if a start_date was chosen.
if self.cleaned_data['start_date']:
sqs = sqs.filter(pub_date__gte=self.cleaned_data['start_date'])
# Check to see if an end_date was chosen.
if self.cleaned_data['end_date']:
sqs = sqs.filter(pub_date__lte=self.cleaned_data['end_date'])
return sqs
DateRange SearchForm is a custom form and has more flexibility as it has more control on developer side. The simplest way to go about creating your own form is to inherit from SearchForm (or the desired parent) and extend the search method. By doing this, you save yourself most of the work of handling data correctly and stay API compatible with the SearchView.
More help can be from this question django haystack custom form

Django: How to set up a single view for multiple urls where each url points to subclass of a base model?

I have the following urls:
browse/college
browse/department
browse/subjects
I have Tag model where College, Department and Subject are all subclasses on Tag. All the urls will call a single view.
I want to create a view called browse_specfic_tag(request, model_name)
I was thinking of converting model name to model using get_model and do something like,
TagModel = get_model(model_name..)
but I am going to tie url name with model which might not be a good thing to do if I decided to rename either of them.
Any better way to do this?
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
…
The proper way of solving this is passing an extra option to the view. See related documentation entry.
Example:
url('^college/$', 'tag_view', {'model': College})
def tag_view(request, model):
records = model.objects.filter(…)
Furthermore, actions should not be included in URL's name. URLs should identify resources. Therefore I would skip the browse part of the URL.

extract or return only the model instance of a foreign key or one to one field from a queryset in django

Does anyone know if it's possible to extract only the model instances of a foreign key or one to one field from a queryset in Django?
Hypothetically lets say I have two classes, a Post and a MagicalPost, linked by a OneToOne Field like so:
class Post(models.Model):
...
class MagicalPost(models.Model):
post = models.OneToOneField('Post')
pony = models.TextField(max_length=100, help_text='This is where the MAGIC happens!')
I'd like to run a query on all magical posts, but I only want to receive the post objects. Right now I'm looping through the queryset in order to extract the posts:
magical_posts = MagicalPost.objects.all()
posts = []
for magical_post in magical_posts:
posts.append(magical_post.post)
return posts
Further down the line the posts get processed by functions that operate on general Post objects so I don't want to deal with the magical_post.post extrapolation, nor do I need the magical attributes.
This doesn't "feel" right. I'm thinking there could be a better way to extract the foreign keys, so if anyone knows of a better/cleaner way to do this I'm all ears; otherwise I'm just going to keep it like this because . . . well . . . it works!
Thanks.
You can easily use select_related for example. It would be only one db query.
magical_posts = MagicalPost.objects.select_related()
# same code
Or use 2 queries like this:
posts_ids = MagicalPost.objects.values_list('post', flat=True)
posts = Post.objects.filter(id__in=posts_ids)