Whoosh + django-haystack missing search results - python

I'm playing around with a relatively straightforward implementation of Whoosh 2.6 and django-haystack 2.3.1 for a search of "contact" objects. However, an example search for "mary" only returns a small subset of my many "Mary" contacts. Here are the relevant files:
search_indexes.py
from django.db.models import Q
from haystack import indexes
from apps.contact.models import Contact
class ContactIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
full_name = indexes.CharField(model_attr='full_name', null=True)
email = indexes.CharField(model_attr='email', null=True)
phone = indexes.CharField(model_attr='phone', null=True)
content_auto = indexes.NgramField(use_template=True)
# several more indexed=False fields to avoid db hits later
def get_model(self):
return Contact
def index_queryset(self, **kwargs):
# one of full_name, email, phone has to be non-null
return self.get_model().objects.filter(
Q(full_name__isnull=False) | Q(email__isnull=False) | Q(phone__isnull=False))
def get_updated_field(self):
return 'date_modified'
contact_text.txt and contact_content_auto.txt (both the same)
{% if object.full_name %}{{ object.full_name }}{% else %}{% endif %}
{% if object.email %}{{ object.email }}{% else %}{% endif %}
{% if object.phone %}{{ object.phone }}{% else %}{% endif %}
views.py
def search(request):
sqs = SearchQuerySet()
form = SearchForm(request.POST, searchqueryset=sqs, load_all=False)
if form.is_valid():
return form.search()
Relevant notes:
some code has been omitted for simplicity
the templates have if statements in order to get rid of the word "None" in the search index
currently, all of my Contact objects have a valid full_name
there is no pattern to which "mary" results are returned and which aren't: select count(*) from contact_contact where lower(full_name) like
'%mary%'; returns 97 rows, while the search returns only 5.
Similarly, for 483 "john" contacts, the search returns 19.
using Python 2.7, Django 1.5 and Postgres 8.4

Related

django Insert or Update record

the insert works but multiple data enters, when two data are inserted when I try to update, it does not update the record
I just want that when the data is already have record in the database, just update. if not, it will insert
def GroupOfProduct(request):
global productOrderList
relatedid_id = request.POST.get("relatedid")
groups = ProductRelatedGroup(id=id)
productOrderList=[]
try:
for products_id in request.POST.getlist("product"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct(
product = products
)
insert_update_groupofproduct.save()
except ProductRelatedGroupAndProduct.DoesNotExist:
for products_id in request.GET.getlist("relatedid"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct.objects.get(id=products)
insert_update_groupofproduct.product = products
insert_update_groupofproduct.save()
return redirect(relatedgroup)
this is my models.py
class Product(models.Model):
product = models.CharField(max_length=500)
class ProductRelatedGroup(models.Model):
category = models.CharField(max_length=500, blank=True)
class ProductRelatedGroupAndProduct(models.Model):
productrelatedgroup = models.ForeignKey(ProductRelatedGroup,on_delete=models.SET_NULL, null=True, blank=True,verbose_name="Product Related Group")
product = models.ForeignKey(Product,on_delete=models.SET_NULL, null=True, blank=True,verbose_name="Product")
UPDATE
I tried this, the insert works fine, but the update does not work
def GroupOfProduct(request):
global productOrderList
groups = ProductRelatedGroup(id=id)
idproduct = request.POST.get('relatedid')
if ProductRelatedGroupAndProduct.objects.filter(id=idproduct).exists():
print("update")
for products_id in request.GET.getlist("relatedid"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct.objects.get(id=products)
insert_update_groupofproduct.product = products
insert_update_groupofproduct.save()
return redirect(relatedgroup)
else:
productOrderList = []
for isa in request.POST.getlist('relatedid'):
productOrderList.append(isa)
i = 0
for i in productOrderList:
for products_id in request.POST.getlist("product"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct(
productrelatedgroup=groups,
product=products
)
insert_update_groupofproduct.save()
return redirect(relatedgroup)
return redirect(relatedgroup)
FLOW OF MY PROGRAM (with picture)
when the admin-user Insert data just (like this)
the batch insert work perfectly fine
and when i tried to update (batch update)
only one data updated
and when i tried to insert again (just like this)
no result
In Insert
<QueryDict: {'csrfmiddlewaretoken': ['F3evgRJwNw4p5XCOVE0qeFhP3gmGG5ay4GBbpoZQg3P5l6TNXHY7KN2lD56s6NCU'], 'relatedid': ['200', '201']}>
This is my html,
{% for relatedgroups in relatedgroups %}
<input type="hidden" name="relatedid" value="{{relatedgroups.id}}">
{% endfor %}
<fieldset class="module aligned ">
<div class="form-row field-user_permissions">
<div>
<div class="related-widget-wrapper">
<select name="product" id="id_user_permissions" multiple class="selectfilter" data-field-name="Product" data-is-stacked="0">
{% for relatedgroups in relatedgroups %}
<option value="{{relatedgroups.product.id}}" selected>{{relatedgroups.product}}</option>
{% endfor %}
{% for product in products %}
<option value="{{product.id}}">{{product.id}}-{{product}}</option>
{% endfor %}
</select>
</div>
</div>
</div>
</fieldset>
when the data is selected or already have data in the database it will shows in Chosen Product box
Please use update_or_create method. This method if a data is exist then updated the details else newly inserted.
Reference:
https://www.kite.com/python/docs/django.db.models.QuerySet.update_or_create
https://djangosnippets.org/snippets/1114/
def GroupOfProduct(request):
group_id = request.POST.get('group')
groups = ProductRelatedGroup(id=group_id)
idproduct = request.POST.get('product')
for products_id in request.POST.getlist("product"):
products = Product(id=products_id)
ProductRelatedGroupAndProduct.objects.update_or_create(id=idproduct,defaults={'product':products,'productrelatedgroup':groups})
return redirect(relatedgroup)
We need to delete all the records first from ProductRelatedGroupAndProduct using the related_ids (product ids which were already selected) and then enter again into ProductRelatedGroupAndProduct using product_ids (product ids which are selected).
Why we need to delete ?
In a situation where you want to remove a product from the selected list, an update would not delete the relation. In-order to remove that product from the selected list we need to delete that relation (not the Product).
def GroupOfProduct(request):
related_ids = request.POST.getlist('relatedid')
product_ids = request.POST.getlist("product")
# delete the existing ProductRelatedGroupAndProduct entries
for product_id in related_ids:
product = Product.objects.filter(id=product_id).first()
obj = ProductRelatedGroupAndProduct.objects.get(product = product)
obj.delete()
# create new records in ProductRelatedGroupAndProduct with the selected product_ids
for product_id in product_ids:
product = Product.objects.filter(id=product_id).first()
obj = ProductRelatedGroupAndProduct(
product = product,
)
obj.save()

Formfield displayed based on treatment group

I am programming an experiment with different treatment Groups. For Treatment Group 3 and 4, I want to know the name of the Participants by using a form field. For the Control Group and Treatment Group 1 - 2, the name is irrelevant and thus, there should not be a forms_field.
I ws already thinking of excluding the form field in HTML by using if loops, so if treatment = 3 or 4, display the field. However, I cannot proceed to the next page for the other Treatment Groups since the field shouldn't be blank. Inserting a blank=True is not really an option since Treatment Group 3 and 4 could just skip the name. I want to "force" Treatment Group 3 and 4 to give me their name.
My code is too long to post it here, so I'll just post the relevant passages:
Modelsview
class Subsession(BaseSubsession):
def creating_session(self):
treatmentgroup = itertools.cycle(['ControlGroup','one','two','three', 'four'])
for p in self.get_players():
p.treatmentgroup = next(treatmentgroup)
class Group(BaseGroup):
pass
class Player(BasePlayer):
name = models.StringField(label="Your Name:")
transcribed_text = models.LongStringField()
levenshtein_distance = models.IntegerField()
guthaben = models.CurrencyField(initial=c(0))
cumulative_guthaben = models.CurrencyField()
Pagesview
class Welcome(Page):
form_model = 'player'
def is_displayed(self):
return self.round_number == 1
class Introduction(Page):
form_model = 'player'
def is_displayed(self):
return self.round_number == 1
class Math(Page):
form_model = 'player'
form_fields = ['name']
def is_displayed(self):
return self.round_number == 1
def before_next_page(self):
if self.round_number == 1:
self.player.guthaben = self.player.guthaben + Constants.endowment
def vars_for_template(self):
return {
'treatmentgroup': self.player.treatmentgroup,
'image_path': 'trophy/{}.png'.format(self.round_number)
}
HTML-Page
{% extends "global/Page.html" %}
{% load otree static %}
{% block title %}
The math task
{% endblock %}
{% block content %}
<p align="justify"> In case you have any questions, please open the door and wait for the experimenter to come to your cubicle.
</p>
{% if treatmentgroup == 'four' or treatmentgroup == 'three' %}
{% formfields %}
{% endif %}
{% next_button %}
{% endblock %}
Thanks in advance!
You can modify a django.forms subclass by using its init method. Your code doesn't show how the form gets instantiated, but somewhere you would have
form = Form_class() # unbound and
form = Form_class( request.POST ) # bound
You would modify these to pass hide_name=True if you do not want the name field displayed.
The form itself would include the method
def __init__(self, *args, **kwargs):
self.hide_name = kwargs.pop('hide_name', False)
super().__init__(*args, **kwargs)
self.hide_name is accessible in any method of the form, and form.hide_name in a template that is rendering it. To avoid validation errors you can remove that that field from the form
...
super().__init__(*args, **kwargs)
if self.hide_name:
self.fields.pop('name')

Django- Using Parameters To Display Entry

My database is setup something like.
class comments(models.Model):
min = models.FloatField()
max = models.FloatField()
comment = models.CharField(max_length=255)
In my view, I grab a float value from the user. I want to take that value use the Min/Max as parameters and if that float value is between the Min/Max, display the comment that's associated to it. At times there might be more then 1 match, however for now I cant even figure out how to do the first step.
Use the filter() method of a model manager with a combination of __lte/__gte lookups:
def comments(request):
value = float(request.GET['value'])
return render(request, 'comments.html',
{'comments': comments.objects.filter(min__lte=value,
max__gte=value)})
And then in the comments.html:
{% for comment in comments %}
<div>{{ comment.comment }}</div>
{% endfor %}

Saving the order of items

My model:
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
order = models.SmallIntegerField()
My template:
{% for c in categories %}
{{ c }} -- <input type="text" name="order[{{ c.id }}]" value="{{ c.order }}">
Submit Button
{% endfor %}
My view:
def category_order(request):
print request.POST
print request.POST.getlist('order[]')
Output:
<QueryDict: {u'order[3]': [u'1'], u'order[1]': [u'1'], u'order[5]': [u'2'], u'order[4]': [u'33'], u'order[2]': [u'2'], u'order[6]': [u'3'], u'csrfmiddlewaretoken': [u'4XjehEwMdNK032342JkYJvBJabunKB'], u'order-btn': [u'order']}>
[]
So my question is how can I loop over every order[N] and get the category ID and its new order value to save it to the database?
Using dict comprehension:
orders = {name.split('[')[1].split(']')[0]: request.POST[name]
for name in request.POST if name.startswith('order[')}
# `orders` is not a dictionary maps `id`s to `order`s.
# Do something with `orders`.
Or using simple for loop:
for name in request.POST:
if not name.startswith('order['): continue
id_ = name.split('[')[1].split(']')[0]
order = request.POST[name]
# Do something with `id_`, `order`
If you are looking at having ordered objects, there is a library, django-ordered-model , that can take care of that. That includes a good, drag-and-drop admin view. For example:
from django.db import models
from ordered_model.models import OrderedModel
class Category(OrderedModel):
name = models.CharField(max_length=50, unique=True)
class Meta(OrderedModel.Meta):
pass

Django - Rating System View & Template

I have a content that I'd like to rate on multiple criteria.
Imagine this kind of model:
class Content(models.Model):
name = models.CharField(max_length=50)
class Criterion(models.Model):
name = models.CharField(max_length=50)
content = models.ForeignKey(Content)
class ContRate(models.Model):
user = models.ForeignKey(User, help_text="Who rated ?")
crit = models.ForeignKey(Criterion)
rate = models.DecimalField()
The user has a page displaying the content.
From this page, he can also rate the content on the criteria set
The rating will be done with Ajax.
Now I'm trying to implement the view & the template
view.py
#...
def viewcont(request, content_id):
"""The user can view a content & rate it"""
content = get_object_or_404(Content, pk=content_id)
RateFormSet = modelformset_factory(ContRate)
formset = RateFormSet(queryset=ContRate.objects.filter(content=content, user=request.user))
objs = {
'content': content,
'forms': formset,
}
return render_to_response('content/content_detail.html', objs
, context_instance=RequestContext(request)
)
#...
content_detail.html
<!-- ... -->
<div id="rating">
<ul>
{% for crit in content.crit_set.all %}
<li>
{{ crit }}
<div class="rateit"
data-rateit-value="the_actual_rating_if_already_there"
data-rateit-ispreset="true"
crit-id="{{ crit.id }}"></div>
</li>
{% endfor %}
</ul>
</div>
<!-- ... -->
Now how can I use the forms formset to display the actual rates ?
And how can I draw an empty form to be posted by Ajax from any clicked star ?
(I know the javascript/jQuery part)
Not sure what the point of the formset is here. The rates are all available via the criteria object, using the reverse foreign key to ContRate in exactly the same way as you've done from Criteria to Content.
To make this as efficient as possible, you probably want to get the relevant ratings in the view and bring them together into a single datastructure:
content = get_object_or_404(Content, pk=content_id)
criteria = content.criteria_set.all()
user_ratings = ContRate.objects.filter(content=content, user=request.user)
ratings_dict = dict((c.crit_id, c.rate) for c in user_ratings)
for crit in criteria:
crit.user_rating = ratings_dict.get(crit.id)
Now you can pass criteria directly to your template, and there you can iterate through it to show the user_rating for each one.
(Final point: "criteria" is plural, the singular is "criterion". :-)

Categories

Resources