Django search vector - no match on exact keyword - django

I have an application where customer can search for products by name, brand etc.
the problem is if you write the exact name or the exact brand name will result in no match,
if you write the half of the name or the id will return a match
view
def home(request):
active_product = Product.objects.all().filter(show=True).order_by('-score', '-brand_name')
#Paginator
paginator = Paginator(active_product, 20)
page = request.GET.get("page")
active_product = paginator.get_page(page)
#Filterd by search
searchProduct = request.GET.get("do_search")
if searchProduct:
active_product = Product.objects.annotate(search=SearchVector('name', 'id', 'sex','brand_name'),).filter(search__icontains=searchProduct)
return render(request, 'index.html', {'productObject': active_product})
Model:
class Brand(models.Model):
name = models.CharField(unique=True, max_length=30, blank=False,null=True)
def __str__(self):
return self.name
class Product(models.Model):
genderChoice = (('W','Female'),('M','Male'),('U','Unisex'))
id = models.BigIntegerField(primary_key=True, null=False)
brand_name = models.ForeignKey(Brand, to_field='name', on_delete=models.CASCADE, related_name='brand_name', default='Brand Name')
name = models.CharField(max_length=300, blank=False,null=False)
sex = models.CharField(max_length=7,choices=genderChoice, default='U')
p_type = models.CharField(max_length=50, blank=True,null=True)
image_link = models.CharField(max_length=500, blank=True,null=True)
stock_price = models.DecimalField(max_digits=10,decimal_places=2, default=999.00)
show = models.BooleanField(default=False)
in_stock = models.BooleanField(default=False)
created = models.DateField(auto_now_add=True)
volume= models.CharField(max_length=50, blank=True,null=True)
score = models.IntegerField(default=1)
price = models.DecimalField(max_digits=100, default=999.00, decimal_places=2)
def __str__(self):
template = '{0.name} {0.brand_name} {0.volume} {0.price}'
return template.format(self)
html:
<form class="my-2 my-lg-0" action="{% url 'home'%}" method="GET">
<div class="input-group mb-3">
<input type="search" class="form-control border-dark" placeholder="search" aria-label="Search" aria-describedby="button-addon2" name="do_search">
<button class="ml-1 btn btn-outline-dark" type="submit" id="button-addon2">Search</button>

The way you use searchvector is wrong. According to the doc about searchvector
replace:
active_product = Product.objects\
.annotate(search=SearchVector('name', 'id', 'sex','brand_name'),)\
.filter(search__icontains=searchProduct)
with
active_product = Product.objects\
.annotate(search=SearchVector('name', 'id', 'sex','brand_name'))\ # no need to have another ","
.filter(search=searchProduct) # remove icontains

Related

hi, i want add search bar to myproject but doesn't return anything

this view for search:
class Search(generic.ListView):
model = Blog
template_name = 'pages/blog_list_view.html'
context_object_name = 'blog'
def get_queryset(self):
search = self.request.GET.get("q")
search_result = Blog.objects.filter(
Q(title__icontains=search) |
Q(description__icontains=search) |
Q(active=True))
return search_result
search form in _base.html
<form action="{% url 'search' %}" method="get">
<input name="q" type="text" placeholder="Search...">
</form>
my model
class Blog(models.Model):
title = models.CharField(max_length=100)
cover = models.ImageField(upload_to='blog_cover/')
description = models.CharField(max_length=200)
text = models.TextField()
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
active = models.BooleanField(default=False)
date_create = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def __str__(self):
return f'{self.title} : {self.author}'
def get_absolute_url(self):
return reverse('blog_detail', args=[self.id])
tip: "I use Persian language for search"
I tried __icontains and __contains but result was same

Django parent.child_set no errors but not showing in html

Views.py defining the context in view
def customers(request, pk):
customer = Customer.objects.get(id=pk)
issues = customer.issue_set.all()
receives = customer.receive_set.all()
context={'customer':customer,'issues':issues,'receives':receives}
return render(request,'accounts/customers.html')
in html
<div class="col-md">
<div class="card card-body">
<h5>Contact Information</h5>
<hr>
<p>Email: {{customer.email}}</p>
<p>Phone: {{customer.phone}}</p>
</div>
</div>
{% for issue in issues %}
<td>{{issue.item}}</td>>
<td>{{issue.item.category}}</td>
<td>{{issue.date_created}}</td>
<td>{{issue.status}}</td>
<td><a href="">UPDATE</td>
<td><a href="">DELETE</td>
{% endfor %}
#Model
class Item(models.Model):
CATEGORY = (
('Gudang Kering', 'Gudang Kering'),
('Gudang Basah','Gudang Basah'),
)
name = models.CharField(max_length=200,null= True)
stock = models.IntegerField(default='0', blank=False, null=True)
category = models.CharField(max_length=200,null= True,choices=CATEGORY)
reorderlevel = models.IntegerField(default='0', blank=False, null=True)
maxreorderlevel = models.IntegerField(default='0', blank=False, null=True)
description = models.CharField(max_length=200,null= True, blank= True)
date_created = models.DateTimeField(auto_now_add= True)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.name
class Issue(models.Model):
STATUS = (
('Pending', 'Pending'),
('Granted','Granted'),
('Denied','Denied'),
)
customer = models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
item = models.ForeignKey(Item, null=True, on_delete= models.SET_NULL)
quantity = models.IntegerField(default='0', blank=False, null=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
status = models.CharField(max_length=200,null= True, choices=STATUS)
def __str__(self):
return self.status + ' ' +str(self.customer)
I tried to get the object by id of the customer in order to make it a dynamic url where the url will depend on str:pk of customer id
i managed to show output data if i do
customer = Customer.objects.all #but that will show all the customer
so i tried as in the view to get the id
and define it with parent.child_set.all
but it doesn't show up,even the text update and delete don't show up in

ValueError at /category/sugar/ Field 'id' expected a number but got 'sugar'

I'm trying to make category for eCommerce
urlpatterns = [
path('category/<cats>/', views.categoryView , name = 'category')
]
views.py:
def categoryView(request, cats):
products_category = Product.objects.filter(category=cats)
return render(request, 'store/categories.html', {'cats': cats.title(), 'products_category': products_category})
models.py
class Category(models.Model):
name = models.CharField(max_length=100)
image = models.FileField(null=True, blank=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('index')
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=40, unique=True)
price = models.FloatField()
discount = models.FloatField(blank=True, null=True)
digital = models.BooleanField(default=False, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category')
featured = models.BooleanField(default=False, null=True, blank=True)
new = models.BooleanField(default=False, null=True, blank=True)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.name
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
html
{% for product in products_category %}
<div class="col-lg-3 col-md-6">
<div class="product-item mb-30">
<a href="#" class="product-img">
<img src="{{ product.imageURL}}" alt="">
<div class="product-absolute-options">
<span class="offer-badge-1">6% off</span>
<span class="like-icon" title="wishlist"></span>
</div>
</a>
</div>
</div>
{% endfor %}
when i try to get category name to my URL got this error, and if i write the category id in URL like this >> http://127.0.0.1:8000/category/3/ I got the correct page, If i clicked on category get this message ValueError at /category/sugar/
Field 'id' expected a number but got 'sugar'.
in urls
path('category/<str:cats>/', views.categoryView , name='category')
in views.py
products_category = Product.objects.filter(category__name=cats)
here category__name is with double "_"
try it.
If you want to have a string in your url, you need to do the following:
path('category/<str:name>/', views.categoryView , name = 'category')
Refer to the Django documentation:
https://docs.djangoproject.com/en/3.0/topics/http/urls/

How can I choose the field of a ForeignKey that is displayed on a ModelForm?

I have the following models:
class DirectoryDoctors (models.Model):
num = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
designation = models.CharField(
choices=design_choices, max_length=30, default='unspecified')
mobile = models.CharField(max_length=15, default='')
alternate = models.CharField(max_length=15, default='', blank=True)
email = models.CharField(max_length=50, default='', blank=True)
dob = models.DateField(null=True, blank=True)
specialty = models.ForeignKey(SpecialtyChoices, on_delete=models.DO_NOTHING,null=True)
institution = models.ForeignKey(DirectoryHospital, on_delete=models.DO_NOTHING)
def __str__(self):
st = f"{self.name}"
return st
class DhanwantriComplaint(models.Model):
num = models.AutoField(primary_key=True)
sender = models.ForeignKey(DirectoryDoctors, blank=False, null=False, on_delete=models.PROTECT)
location = models.ForeignKey(DirectoryHospital, blank=False, null=False, on_delete=models.PROTECT)
complainttype = models.ForeignKey(DhanwantriComplaintCode, blank=False, null=False, on_delete=models.PROTECT)
details = models.CharField(max_length=10000)
since = models.CharField(max_length=100, blank=True, null=True)
alertDNO = models.BooleanField(default=True)
alertZNO = models.BooleanField(default=True)
alertSNO = models.BooleanField(default=True)
ITMinformed = models.BooleanField(default=False)
ITMvisited = models.BooleanField(default=False)
prevticketid = models.CharField(max_length=100, blank=True, null=True)
raisedon = models.DateTimeField(default=timezone.now)
lastupdate = models.DateTimeField(default=timezone.now)
closed = models.BooleanField(default=False)
closedon = models.DateTimeField(blank=True, null=True)
I have the Modelform:
class DhanwantriComplaintForm(ModelForm):
class Meta:
model = DhanwantriComplaint
fields = [
'sender',
'location',
'complainttype',
'details',
'since',
'alertDNO',
'alertZNO',
'alertSNO',
'ITMinformed',
'ITMvisited',
'prevticketid',
]
widgets = {
'details': forms.Textarea(attrs={
'rows': 10,
'cols': 15
}),
'sender': forms.TextInput(),
}
And the view:
#login_required
def complaint_dhanwantri_new(request):
items = LinkSection.objects.all()
docuser = DoctorUser(request)
print(f'docuser is {docuser}. type is {type(docuser)}')
form = DhanwantriComplaintForm(
initial={
'sender': docuser,
'location': docuser.institution,
}
)
if request.method == 'POST':
print(f'Received POST: {request.POST.get}')
form = DhanwantriComplaintForm(request.POST)
if form.is_valid():
print("Form is valid")
else:
print("Form is not valid")
return render(
request, 'app/complaints/complaint.html', {
'rnd_num': randomnumber(),
'fileitems': items,
'form': form,
'docuser': docuser,
'total_docs': DirectoryDoctors.objects.count(),
'total_institutions': DirectoryHospital.objects.count()
})
And the following code in my template:
<div class="form-group row">
<label for="inputEmail3" class="col-sm-3 col-form-label">Sender: </label>
<div class="col-sm-21">
{% render_field form.sender|append_attr:"readonly:readonly" type="text" class+="form-control" %}
</div>
</div>
<div class="form-group row">
<label for="inputEmail3" class="col-sm-3 col-form-label">Location: </label>
<div class="col-sm-21">
{{ form.location|add_class:"form-control" }}
</div>
</div>
The problem is that when the form is rendered, instead of dislaying the field name of model DirectoryDoctors, the pk value is displayed as below.
How can I control what field is displayed when the form is shown?
It is because sender is a foreign key in DhanwantriComplaint model. It can only be populated with certain values (primary keys of DirectoryDoctors model). So naturally it should be a choice field (rendered as dropdown) with certain options. Django renders FK fields as dropdowns with __str__ representation of related model as display and PK as value by default. But you are forcing django here
'sender': forms.TextInput(),
to render it as text field. And because original value of this field is just a number (FK), it shows that number in field.
However if you want to user TextInput for foreign key, you have to modify your forms behavior like this
def __init__(self, initial=None, instance=None, *args, **kwargs):
if initial is None:
initial = {}
if 'sender' in initial:
initial['sender'] = initial['sender'].name
elif instance is not None:
initial['sender'] = instance.sender.name
super(PatientForm, self).__init__(initial=initial, instance=instance, *args, **kwargs)
def clean(self):
cleaned_data = super(PatientForm, self).clean()
sender = cleaned_data.pop('sender')
sender = DirectoryDoctors.objects.filter(name=sender).first()
if sender is None:
raise forms.ValidationError('Sender does not exist')
cleaned_data['sender'] = sender
return cleaned_data
The constraint on above solution is that DirectoryDoctors's name should be unique. Otherwise it can/will create a mess.

Display ForeignKey attributes in template

I have the following models:
# models.py
class Site(models.Model):
name = models.CharField(max_length=75)
slug = models.SlugField(_('slug'), max_length=75, blank=True, null=True)
link = models.CharField(max_length=150)
created_on = models.DateTimeField(auto_now_add=True, editable=False)
modified_on = models.DateTimeField(auto_now=True)
class SiteRatings(models.Model):
site = models.ForeignKey('Site', related_name='siterating', blank=True, null=True)
overall_rating = models.FloatField(blank=True, null=True)
plans_rating = models.FloatField(blank=True, null=True)
prices_rating = models.FloatField(blank=True, null=True)
design_rating = models.FloatField(blank=True, null=True)
support_rating = models.FloatField(blank=True, null=True)
def save(self, *args, **kwargs):
self.overall_rating = (self.plans_rating + self.prices_rating + self.design_rating + self.support_rating)/4
super(SiteRatings, self).save(*args, **kwargs)
def __str__(self):
return self.site.name
My views
# views.py
def home(request):
print(user_language)
site_list = Site.objects.order_by('-date_launched')
return render_to_response('index.html', {'site_list': site_list}, RequestContext(request))
In template
# template
{% for site in site_list %}
<h4><span class="label label-info">{{ site.name }}</span></h4>
<h4><span class="label label-info">{{ site.siterating.overall_rating }}</span></h4>
{% endfor % }
The problem is that when I try to access {{ site.siterating.overall_rating }} nothing is being returned, I tried to change for {{ site.siterating.0.overall_rating }} and it is not displaying yet.
I solved my problem. First in models I created a method to get the site rating (thank you karthikr for your suggestion):
#models
class Site(models.Model):
name = models.CharField(max_length=75)
slug = models.SlugField(_('slug'), max_length=75, blank=True, null=True)
link = models.CharField(max_length=150)
created_on = models.DateTimeField(auto_now_add=True, editable=False)
modified_on = models.DateTimeField(auto_now=True)
def get_site_rating(self):
return self.siterating.filter(site = self)
After, I went to my template and used the class method that I had created:
{% for site in site_list %}
<h4><span class="label label-info">{{ site.name }}</span></h4>
<h4><span class="label label-info">{{ site.get_site_rating.0.overall_rating }}</span></h4>
{% endfor % }
Please, note that I used ...get_site_rating.0... just because in my case I have just 1 rating/site.