Django-haystack has examples of how to make one app searchable. We'll that is great!
However, when you have more than one app and each one is related to the User, how would you go about
having haystack (faceted) to allow you search for what you want. Let's say on all three of these models.
Example: Show me all male users who have a keyword of "experienced" in their description who also have
a skill with the name "Analyst" whose Info keywords contains "bla".
I googled with no result. So, I am looking at bringing few apps under the same search page.
class UserProfile(models.Model):
GENDER_MALE = 1
GENDER_FEMALE = 2
GENDER_CHOICES = (
(GENDER_MALE, 'Male'),
(GENDER_FEMALE, 'Female'),
)
user = models.OneToOneField(User, related_name="%(class)s", unique=True)
full_name = models.CharField(
_("Full name"),
max_length=200,
blank=True,
)
gender = models.IntegerField(
_('Gender'),
choices=GENDER_CHOICES,
blank=False,
null=True,
)
# common
country = CountryField(
_('Country'),
null=True,
blank=False,
)
# common
about = models.TextField(
_('About Me'),
blank=True,
validators=[MaxLengthValidator(400)],
)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __unicode__(self):
return u'%s' % (self.user.username)
class Skill(models.Model):
user = models.ForeignKey(
User,
related_name="%(class)s"
)
name = models.CharField(
_('Skill Name'),
max_length=70,
null=False
)
category = models.ForeignKey(
'self',
blank=True,
null=True
)
is_active = models.BooleanField(
default=True
)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __unicode__(self):
return u'%s' % (self.name)
class Info(models.Model):
user = models.ForeignKey(
User,
related_name="%(class)s",
null=False
)
description = models.TextField(
blank=False,
)
keywords = models.CharField(
blank=True,
null=True,
max_length=56,
)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __unicode__(self):
return u'%s' % self.title
I think if i am not wrong your question is to how to make facets work for multiple apps.
In url.py try something like
sqs = SearchQuerySet().facet('field1').facet('field2').facet('field3')
urlpatterns += patterns('haystack.views',
url(r'^$', FacetedSearchView(form_class=FacetedSearchForm, searchqueryset=sqs), name='haystack_search'),)
In search.html
{% if facets.fields.field1 %} {% for type1 in facets.fields.field1 %}
{{ type1.0 }} ({{ type1.1 }}) {% endfor %} {% else %}
No type1 facets.
{% endif %}
{% if facets.fields.field2 %} {% for author in facets.fields.field2 %}
{{ type2.0 }} ({{ type2.1 }}) {% endfor %} {% else %}
No type2 facets.
{% endif %}
{% if facets.fields.field3 %} {% for author in facets.fields.field3 %}
{{ type3.0 }} ({{ type3.1 }}) {% endfor %} {% else %}
No type3 facets.
{% endif %}
Your Apps should have individual search_index files .
Check the output of rebuild_index and update_index, whther indexing is done properly or not.
Related
Can anyone explain me, why i get Post.CommentPost.None?
How to correct connect CommentPost with Posty in query? I need get {{posty.comments.user}} my result is Post.CommentPost.None
Here something about my models and functions.
class Posty(models.Model):
title = models.CharField(max_length=250, blank=False, null=False, unique=True)
sub_title = models.SlugField(max_length=250, blank=False, null=False, unique=True)
content = models.TextField(max_length=250, blank=False, null=False)
image = models.ImageField(default="avatar.png",upload_to="images", validators=[FileExtensionValidator(['png','jpg','jpeg'])])
author = models.ForeignKey(Profil, on_delete=models.CASCADE)
updated = models.DateTimeField(auto_now=True)
published = models.DateTimeField(auto_now_add=True)
T_or_F = models.BooleanField(default=False)
likes = models.ManyToManyField(Profil, related_name='liked')
unlikes = models.ManyToManyField(Profil, related_name='unlikes')
created_tags = models.ForeignKey('Tags', blank=True, null=True, related_name='tagi', on_delete=models.CASCADE)
test_wyswietlenia = models.IntegerField(default=0, null=True, blank=True)
class CommentPost(models.Model):
user = models.ForeignKey(Profil, on_delete=models.CASCADE)
post = models.ForeignKey(Posty, on_delete=models.CASCADE, related_name="comments")
content1 = models.TextField(max_length=250, blank=False, null=False)
date_posted = models.DateTimeField(default=timezone.now)
date_updated = models.DateTimeField(auto_now=True)
VIEWS
tag = request.GET.get('tag')
if tag == None:
my_tag = Posty.objects.prefetch_related('comments')
my_view = Posty.objects.prefetch_related('my_wyswietlenia')
else:
my_tag = Posty.objects.filter(created_tags__tag=tag)
my_view = Posty.objects.prefetch_related('my_wyswietlenia')
TEMPLATES
{% for post in my_tag %}
{% if post.comments.last.user == None %}
<span class="forum_tag_author">Komentarz » Brak komentarza</span><br/>
<span class="forum_tag_author">Stworzony przez » {{post.author}} </span><hr/>
{% else %}
<span class="forum_tag_author">Komentarz » {{post.comments.last.content1}}</span><br/>
<span class="forum_tag_author">Odpowiadający » Dodany przez: {{post.comments.last.user}} </span><hr/>
{% endif %}
{% endfor %}
And this
{{post.comments}} give a request Post.CommentPost.None
What is problem? What i do bad?
You can use a Subquery expression [Django-doc] to obtain the latest content1 comment and author with:
from django.db.models import OuterRef, Subquery
last_comment = CommentPost.objects.filter(post=OuterRef('pk')).order_by('-date_posted')
my_tag = Posty.objects.annotate(
last_comment=Subquery(last_comment.values('content1')[:1]),
last_comment_user=Subquery(last_comment.values('user__name')[:1]),
last_comment_user_pk=Subquery(last_comment.values('user')[:1])
).prefetch_related('my_wyswietlenia')
The __name might be different, since it depends on the fields of you Profil model.
Then you can render the content and the last author with:
{% for post in my_tag %}
{{ post.last_comment }} by {{ post.last_coment_user }}
{% enfor %}
I want to access the region of the user in my template:
{% if object.seller.profile.post_code %}{{ object.seller.profile.post_code }} {% endif %}{% if object.seller.profile.city%}{{ object.seller.profile.city }}<br/>{% endif %}
{% if object.seller.profile.region.name %}{{ object.seller.profile.region.name }}, {% endif %}{% if object.seller.profile.region.country.name %}{{ object.seller.profile.region.country.name }}{% endif %}
This is my model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics',
verbose_name='Profilbild')
post_code = models.CharField(max_length=5, blank=True, verbose_name='Postleitzahl')
region = models.ForeignKey(Region, on_delete=models.CASCADE,
null=True, blank=True, verbose_name='Region', help_text='Bundesland oder Kanton')
city = models.CharField(max_length=50, blank=True, verbose_name='Stadt')
street = models.CharField(max_length=50, blank=True, verbose_name='Straße')
class Region(models.Model):
name = models.CharField(max_length=30, verbose_name='Region', help_text='Bundesland oder Kanton', default='Berlin')
symbol = models.CharField(max_length=30, verbose_name='Region-Kürzel', default='BER')
country = models.ForeignKey('Country', on_delete=models.CASCADE, null=True,
verbose_name='Land', default=0)
class Country(models.Model):
name = models.CharField(max_length=30, verbose_name='Land', default='Deutschland')
symbol = models.CharField(max_length=4, verbose_name='Kürzel', default='DE')
But I don't want to force the user to enter their adress data. But if the ForeignKey-Fields are not filled I get
'NoneType' object has no attribute 'country'
Is there a more a elegant way than checking each field if it is filled in the views.py and adding it separately to the context?
Can add some shortcuts to your Profile model to help with this:
views.py:
class Profile(models.Model):
...
#property
def country_name(self):
return self.region.country.name if self.region and self.region.country else None
template.html:
...
<div> {{object.seller.profile.country_name }} </div>
def Search(request):
if request.method == 'GET' and request.GET['x']:
parameter = request.GET['x']
results = Category.objects.filter(advert__Seller_Name__icontains = parameter)
return render(request, 'campusbuy/search.html', {'results': results})
else:
return render(request, 'campusbuy/search.html')
Above is my search function. When I try to search for an object in my template, it returns nothing. However, when I deliberately search for a Seller_name that's not in the db it returns the {% else %} value. Below is the template:
% extends 'campusbuy/base.html' %}
{% block content %}
{% if results %}
{% for ads in results.advert_set.all %}
<p>{{ads.Seller_Name }}</p>
<p>{{ads.Location}}</p>
<p>{{ads.Description}}</p>
<p>{{ads.Asking_Price}}</p>
{% endfor %}
{% else %}
<p>No Ad matched your search criteria.</p>
{% endif %}
{% endblock %}
Here's the models.py:
class Category(models.Model):
Name = models.CharField(max_length=20, null=True, blank=True)
Details = models.CharField(max_length=100, default="Default")
Category_Logo = models.ImageField(max_length=100, upload_to='uploads')
def __str__(self):
return self.Name
class Advert(models.Model):
HALL3 = 'HALL3'
HALL4 = 'HALL4'
HALL2 = 'HALL2'
MAIN_GATE = 'MAINGATE'
HALL1 = 'HALL1'
Location_Choices = (
(HALL3, 'Hall3'),
(HALL4, 'Hall4'),
(HALL2, 'Hall2'),
(MAIN_GATE, 'Main_gate'),
(HALL1, 'Hall1')
)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Seller_Name = models.CharField(max_length=50, blank=False, null=False)
Phone_Number = models.CharField(max_length=11, blank=False, null=False,
help_text='<p style="color: red; font: italic 12px tahoma;">**Please input a working Phone Number that you can be contacted with on the fly</p>')
image = models.ImageField(max_length=100, upload_to='uploads')
Item = models.CharField(max_length=20, blank=False, null=False)
Location = models.CharField(max_length=10, choices=Location_Choices, default=HALL3, blank=False)
Description = models.TextField(max_length=250, blank=False, null=False)
Asking_Price = models.CharField(max_length=20, blank=False, null=False)
published_date = models.DateTimeField(blank=False, default=timezone.now)
Thank you in advance!
I still haven't understood why you've tried to include Category in the query, since you are not using it at all either in the query itself or in the template. Just query and use Adverts directly:
results = Advert.objects.filter(Seller_Name__icontains=parameter)
and iterate over it also directly:
{% for ads in results %}
<p>{{ads.Seller_Name }}</p>
<p>{{ads.Location}}</p>
<p>{{ads.Description}}</p>
<p>{{ads.Asking_Price}}</p>
{% endfor %}
Also note, you don't need the if block, because the for template tag has an [empty`](https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#for-empty) clause that is displayed if the loop was empty:
{% for ads in results %}
<p>{{ads.Seller_Name }}</p>
<p>{{ads.Location}}</p>
<p>{{ads.Description}}</p>
<p>{{ads.Asking_Price}}</p>
{% empty %}
<p>No Ad matched your search criteria.</p>
{% endfor %}
I want to list all of a teacher's students that have commented on each genre of blog posts. Below is my best effort, but I duplicate student names for each genre, so if they comment on multiple scary blog posts, their names are listed multiple times. How can I list each student name once next to each genre on the teacher profile page?
Models.py
class Genre(models.Model):
name = models.CharField(max_length=200, unique=True)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile')
username = models.CharField(max_length=128, unique=True)
teacher = models.ForeignKey('Teacher', blank=True, null=True)
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile')
name = models.CharField(max_length=128, blank=True, unique=True)
class BlogPost(models.Model):
genre = models.ForeignKey(Genre, on_delete=models.CASCADE, null=True)
class Comment(models.Model):
blogpost = models.ForeignKey(BlogPost, related_name='comments', on_delete = models.CASCADE, null=True)
message = CharField(max_length=1000, blank=True)
commenter = models.ForeignKey(User, related_name='comments', on_delete=models.CASCADE, null=True)
Views.py
def teacher_profile(request):
student_list = Student.objects.filter(teacher__user=request.user)
student_blogpost_list = BlogPost.objects.filter(comments__commenter__student_profile__teacher__user=request.user).distinct()
student_genre_list = Genre.objects.filter(blogpost__in=student_blogpost_list).distinct()
return render(
request,
'myapp/teacher_profile.html',
context= {'student_list':student_list, 'student_blogpost_list':student_blogpost_list, 'student_genre_list':student_genre_list},
)
teacher_profile.html
{% if student_genre_list %}
<h4>Genres</h4>
{% for genre in student_genre_list %}
Genre: {{ genre.name }} - Students:
{% for blogpost in genre.blogpost_set.all %}
{% for comment in blogpost.comments.all %}
{% if comment.commenter.student_profile in student_list %}
{{ comment.commenter.student_profile.username }}
{% endif %}
{% endfor %}
{% endfor %}
<br>
{% endfor %}
{% endif %}
I guess it can be done in following way:
student_list = Student.objects.filter(teacher__user=request.user)
student_blogpost_list = BlogPost.objects.filter(comments__commenter__student_profile__teacher__user=request.user).distinct()
data = dict()
for blogpost in student_blogpost_list:
students_usernames = list(blogpost.comment_set.filter(commenter__in=student_list).
values_list('commenter__student_profile__username', flat=True))
if blogpost.genre.name in data:
data[blogpost.genre.name].append(students_usernames)
else:
data[blogpost.genre.name] = students_usernames
for key in data.keys():
data[key] = set(data[key]) # remove duplicates from list
data variable will be equal to something like:
{'genre_1': {'username_1', 'username_2'}, 'genre_2': {'username_2', 'username_3', 'username_4'}}
My point is that there should not be a lot of logic in template. Using this approach you'll just need to iterate over result dictionary.
Nevertheless, I can't guarantee that this code is working, as I didn't set up database with the same scheme.
I've defined the following models:
class Topic(models.Model):
class Meta:
ordering = [
'title']
objects = models.Manager()
highlighted = HighlightedTopicManager()
highlight = models.BooleanField(
default=False,
help_text='Show this topic on the home page?',
db_index=True)
highlight_order = models.PositiveSmallIntegerField(
default=0,
help_text='In what order do you want this to be added on the home page?'\
' Leave blank for alphabetic order.',
db_index=True)
title = models.CharField(
max_length=2048,
db_index=True)
slug = models.SlugField(
max_length=128,
db_index=True)
excerpt = models.TextField(
null=True,
blank=True)
description = models.TextField()
def _get_content(self):
if self.excerpt:
return self.excerpt
return self.description
content = property(_get_content)
#models.permalink
def get_absolute_url(self):
return ('academic_projects_topic_detail', (), {'slug': self.slug})
def __unicode__(self):
return self.title
class Project(models.Model):
class Meta:
ordering = [
'topic',
'modified',
'created']
objects = models.Manager()
highlighted = HighlightedProjectManager()
highlight = models.BooleanField(
help_text='Highlight this in the projects\' main page?'\
' Only the most recently modified one will be displayed.')
redirect_to = models.URLField(
blank=True,
null=True,
help_text='Use this for old or extenal projects.')
short_title = models.CharField(
max_length=1024,
db_index=True)
slug = models.SlugField(
max_length=128,
db_index=True)
title = models.CharField(
max_length=2048,
db_index=True)
created = models.DateTimeField(
auto_now_add=True)
modified = models.DateTimeField(
auto_now=True)
excerpt = models.CharField(
max_length=1024,
null=True,
blank=True,
help_text='Concise description to show in the listing page.')
description = models.TextField(
null=True,
blank=True,
help_text='This content will be rendered right after the title.')
downloads = models.ManyToManyField(
Download,
null=True,
blank=True,
help_text='Downloadable files')
footer = models.TextField(
null=True,
blank=True,
help_text='This content will be rendered at the bottom of the page.')
people = models.ManyToManyField(
Person,
help_text='People involved in this project.',
related_name='projects')
organizations = models.ManyToManyField(
Organization,
help_text='Organizations involved other than the lab.',
blank=True,
null=True,
related_name='projects')
publications = models.ManyToManyField(
Publication,
blank=True,
null=True)
topic = models.ForeignKey(
Topic,
verbose_name=_('Main topic'),
help_text='This is the main topic.',
related_name='projects')
sponsors = models.ManyToManyField(
Sponsor,
blank=True,
null=True,
help_text='sponsored_projects')
related_topics = models.ManyToManyField(
Topic,
null=True,
blank=True,
help_text='Optional related topics.',
related_name='secondary_projects')
def __unicode__(self):
return self.short_title
#models.permalink
def get_absolute_url(self):
return ('academic_projects_project_detail', (), {'slug': self.slug})
And use the following template to produce this page (http://seclab.cs.ucsb.edu/academic/projects/):
{% extends "academic/project_base.html" %}
{% block content_title %}Projects{% endblock %}
{% block title %}Projects - {{block.super}}{% endblock %}
{% block content %}
{% regroup object_list|dictsort:"topic" by topic as topic_list %}
{% for topic in topic_list %}
<h2 id="{{ topic.grouper.slug }}">{{ topic.grouper }} #</h2>
{% for project in topic.list %}
<h3>{{ project }}</h3>
<p>{{ project.title }}</p>
{% endfor %}
{% endfor %}
{% endblock %}
The view behind this is a generic one, and invoked as:
url(r'^$',
cache_page(ListView.as_view(
queryset=Project.objects.order_by('topic'),
template_name='academic/project_list.html')),
name='academic_projects_project_list'),
So, Projects are already sorted by Topic. Unfortunately, this code yields to duplicate gorups and, sometimes, the groups change at each refresh (or, at least, they change when I reboot the server).
Any idea of why is this happening? The entire code, besides templates', resides here: https://bitbucket.org/phretor/django-academic/src/
The dictsort filter is only for lists of dicts, you don't need it at all here. Your template should read
{% regroup object_list by topic as topic_list %}