I've recently been trying to implement full text search on my webapp with Django Haystack (v2.1.0) with Elasticsearch (v0.90.5) as my search engine. My goal is to be able to, with one query, search through a title field, key line field, and an authors field for a given Song object. Here is my code:
Models.py
class Song(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
key_line = models.CharField(max_length=300, blank=True)
def __unicode__(self):
return self.title
class Author(models.Model):
first_name = models.CharField(max_length=30)
middle_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail')
def __unicode__(self):
if self.middle_name:
return u'%s %s %s' % (self.first_name, self.middle_name, self.last_name)
else:
return u'%s %s' % (self.first_name, self.last_name)
class Meta:
ordering = ('last_name','first_name')
Search_index.py
from haystack import indexes
from songs.models import Song
class SongIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
content_auto = indexes.EdgeNgramField(model_attr='title')
content_auto = indexes.EdgeNgramField(model_attr='key_line')
authors = indexes.MultiValueField()
content_auto = indexes.EdgeNgramField(model_attr='authors')
def get_model(self):
return Song
def prepare_authors(self, object):
return [author.last_name for author in object.authors.all()]
def index_queryset(self, using=None):
return self.get_model().objects.all()
song_text.txt
{{ object.title}}
{{ object.key_line}}
{% for author in object.authors.all %}
{{ author.last_name}}
{% endfor %}
I'm sure my search_index.py is terribly wrong for the authors but I was getting desperate. I've been able to get the title and the key_line to work properly in that if i type in a query, I get results that have the query in the title or have the query in the key line. Performing the same test but instead querying the author's last name doesn't give me results with that author.
I've gone through the documentation and though I've found some information on indexing related fields, I haven't been able to find anything specific related to using EdgeNGrams with a related field. Is this possible to do? Ideally, I'd like to be able to query over the author's first, middle, and last name.
Thanks in advance for any help you can give! Let me know if you need any more information.
Similar problem is addressed # Haystack search on a many to many field is not working
def prepare_category(self, obj):
return [category.name for category in obj.category_set.all()]
Your code should be ;
def prepare_authors(self, object):
return [author.last_name for author in object.authors_set.all()]
Change song_text.txt to ;
{{ object.title}}
{{ object.key_line}}
{% for author in object.authors.all %}
{{ author.first_name}}
{{ author.middle_name}}
{{ author.last_name}}
{% endfor %}
Changing object.authors.all() to object.authors_set.all() should solve this problem.
Related
I have a following models.py for my Django blog, I made a following views.py to pass the value of the slug for my URL parameter.
However I am struggling to create a model in views to get other data(person & description) from Category class.
I have tried some patterns by myself but can not pass them to HTML. (always Error or not showing)
Can you please give me some idea of how to solve this.
models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
views.py
def blog_category(request, category):
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
context = {"category": category, "posts": posts}
return render(request, "blog_category.html", context)
HTML(Localhost:8000/slug)
{{ person }}
{{ description }}
this is full code of my models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
class Recommender(models.Model):
recommender_name = models.CharField(max_length=20)
slug = models.SlugField()
def __str__(self):
return self.recommender_name
class Post(models.Model):
book_title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
book_link = models.CharField(max_length=255)
recommenders = models.ForeignKey("Recommender", on_delete=models.CASCADE,)
source = models.TextField()
source_link = models.CharField(max_length=255)
created_on = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField("Category", related_name="posts")
slug = models.SlugField()
def __str__(self):
return self.book_title
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
Is going to return a queryset. It can have more than one instance of the model class (since you are using filter). In your context you are sending this queryset as posts to your templates.
So in your HTML you can use something like this. You need to use a for loop since there can be more than one item in posts.
{% for post in posts %}
{% for category in post.categories.all %}
{{ category.person }}
{{ category.description }}
{% endfor %}
{% endfor %}
I would look at this example.
Namely, if you render the template like it is shown in the example, you should be able to do
{{ category.person }} {{ category.description }}
Have a quick question. Trying to use a relational model in one DetailView. However, no matter what I try the data does not display. I've tried a few versions of template tags to no avail.
html
{% for parts in relatedparts %}{{ parts.name }}
</div>{% endfor %}
views.py
class ErrorCodeView(DetailView):
context_object_name = 'error_code_details'
model = models.ErrorCodes
template_name = 'error_code_details.html'
def get_context_data(self, **kwargs):
# xxx will be available in the template as the related objects
context = super(ErrorCodeView, self).get_context_data(**kwargs)
context['relatedparts'] = RelatedParts.objects.filter(name=self.get_object())
return context
models.py
class ErrorCodes(models.Model):
name = models.CharField(max_length=256)
description = models.CharField(max_length=400)
instructions = models.CharField(max_length=256)
PartsNeeded = models.CharField(max_length=120, default='')
usercomments = models.CharField(max_length=400, default='', blank=True)
relpic = models.ImageField(upload_to='media/',blank=True)
relpictwo = models.ImageField(upload_to='media/',blank=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("errorcodes:errorcodeview",kwargs={'name':self.name})
class RelatedParts(models.Model):
name = models.CharField(max_length=256)
related_error_code = models.ForeignKey(ErrorCodes, on_delete=models.PROTECT)
def __str__(self):
return self.name
You don't need to do this at all. You can follow the relationship in the template.
{% for part in object.relatedparts_set.all %}{{ part.name }}{% endfor %}
You don't need any code in the view to enable this.
could it be that "name=self.get_object()" should be "name=self.get_object().name" ?
You currently have:
context['relatedparts'] = RelatedParts.objects.filter(name=self.get_object())
but that is probably producing an empty queryset.
Hi I have to following Models.py
class Question(models.Model):
user = models.ForeignKey(User)
article = models.ForeignKey(Article)
number = models.CharField("문제번호", max_length=10, null=True, blank=True)
q_type = models.CharField("문제유형", max_length=15)
question = models.TextField("문제내용")
answer = models.TextField("답안", null=True, blank=True)
reference = models.TextField("참고사항", null=True, blank=True)
def __str__(self):
return "%s번: %s" % (self.number, self.question)
class Professor(models.Model):
question = models.OneToOneField(Question, null=True, blank=True, related_name="related_professor")
articles = models.ManyToManyField(Article, null=True, blank=True)
name = models.CharField("교수이름", max_length=20)
def __str__(self):
return "%s" % (self.name,)
Views.py:
def read_q_table(request, board_id, article_id):
article = get_object_or_404(Article, id=article_id)
context = {
"boards" : Board.objects.all(),
"board_id" : board_id,
"questions" : Question.objects.all().order_by("number"),
"article" : article,
"professor" : Professor.objects.all()
}
return render(request, "q_table.html", context)
Template:
{% if questions %}
{% for question in questions %}
<div class="list-group-item">
<div class="row text-center">
<div class="col-xs-2 col-md-2">{{ question.related_professor.name }}</div>
</div>
</div>
{% endfor %}
{% else %}
What I want is to access professor name field in template. I have passed in context the list of "question" class objects to template, and I would like to use OneToOneField attribute somehow to access related professor name in template.
According to Accessing Django OneToOneField in templates?, {{ question. related_professor.name }} should work, but it doesn't. Can anyone help?
Thanks.
Or, it could be a problem with saving the professor data in the first place. The following code takes form input and submit them to save them.
Professor.objects.get(name=professor).question = question_instance
Professor.objects.get(name=professor).save()
These two lines in def submit_question may raise an eyebrow but I am not sure. Here I am trying to get the related professor instance and update its question field(a OneToOneField that relates to Question class).
def submit_question(request, board_id, article_id):
article = get_object_or_404(Article, id= article_id)
try:
professor = request.POST["professor"].strip()
q_type = request.POST["q_type"].strip()
question = request.POST["question"].strip()
number = request.POST["number"].strip()
reference = request.POST["reference"].strip()
if request.POST['q_type']== "주관식":
answer = request.POST['d_answer'].strip()
else:
answer = request.POST['m_answer'].strip()
except KeyError:
request.session["error"] = "올바른 요청이 아닙니다."
return redirect("read_article", board_id, article_id)
if professor and q_type and question:
question_instance = article.question_set.create(q_type = q_type, question = question, user_id = request.user.id)
Professor.objects.get(name=professor).question = question_instance
Professor.objects.get(name=professor).save()
if number:
question_instance.number = number
if answer:
question_instance.answer = answer
if reference:
question_instance.reference = reference
question_instance.save()
else:
request.session["error"] = "출제교수, 문제유형 및 문제는 필수 입력 항목입니다."
return redirect("read_article", board_id, article_id)
return redirect("read_q_table", board_id, article_id)
These two lines are suspect:
Professor.objects.get(name=professor).question = question_instance
Professor.objects.get(name=professor).save()
The second line will get the Professor from the database again, and it won't have your question instance attached anymore when you save it. Try this instead:
prof = Professor.objects.get(name=professor)
prof.question = question_instance
prof.save()
I have two tables, Author and Book, Each book is written by a mutliple author, by against an author can create a (one) book, I want to get the book list created by a single author.
In my template, I would view the authors who have written one book
my models.py
class Book(models.Model):
user = models.OneToOneField(User,related_name='user')
name = models.CharField(default=0)
def __unicode__(self):
return u'%s' % self.user
class author(models.Model):
name_auth = models.CharField(max_length=256)
Book = models.ForeignKey(compte)
def __unicode__(self):
return u'%s' % (self.name_auth)
my view.py
Book1 =Book.objects.filter(user=request.user).values()
Book1_id= Book1.id
author = Author.objects.filter(pk=Book1_id).values
reponse[author]=author
my template
{% for t in author %}
<td>{{ t.name }}</td>
{% endfor %}
The logic in your models is incorrect given your statement...
"A book can have multiple authors"
You need to use a ManyToManyField to represent the authors of a book:
class Author(models.Model):
user = models.OneToOneField(User)
def __unicode__(self):
return self.user.get_full_name()
class Book(models.Model):
authors = models.ManyToManyField(Author)
name = models.CharField(max_length=500)
def __unicode__(self):
return self.name
# views
from django.shortcuts import render, get_object_or_404
def book_detail(request, book_id):
book = get_object_or_404(Book, id=book_id).prefetch_related('authors')
return render(request, 'book_detail.html', {'book': book})
# book_detail.html
{% for author in book.authors.all %}
{{ author }}
{% endfor %}
When I got this error I understood that I didn't learn URL -HTML- views-model relationship. First, let me show my codes.
This is my views.py:
def category_detail(request, category_name):
links = Link.objects.filter(category__name=category_name)
return render_to_response("category_detail.html", {"links":links}, context_instance=RequestContext(request))
This is models.py:
class Category(models.Model):
name = models.CharField(_("Category"), max_length=255)
user = models.ManyToManyField(User)
def __unicode__(self):
return "%s %s" %(self.user, self.name)
def admin_names(self):
return ', '.join([a.username for a in self.user.all()])
admin_names.short_description = "User Names"
def get_absolute_url(self):
return "/category/%s" % self.name
class Link(models.Model):
user = models.ForeignKey(User)
posted_at = models.DateTimeField(auto_now_add=True)
url = models.URLField()
title = models.CharField(max_length=255, null=True, blank=True)
category = models.ForeignKey(Category)
def __unicode__(self):
return "%s %s %s" %(self.url, self.title, self.category)
This is HTML page:
<div id="profilemenu">
index<p>
{% for category in categories %}
<p>{{category.name }}
{% endfor %}
<p>
</div>
and urls.py:
url(r'^category/(?P<category_name>.*)', 'link.views.category_detail', name="category_detail"),
When I click a category name to open category_detail.html, the URL in browser is like :
http://127.0.0.1:8000/category`/
I can't get categoryname. Please can you tell me my stupid mistake? :\ Thanks for time.
If you are using the namespace in your urls you would need to reference it without quotes in the template.
<p>{{category.name }}
Note: You'll want to ensure the namespace is fully qualified. If you have embedded namespaces you should separate them with :.
<p>{{category.name }}
Hopefully this solves your problem.
You should follow get_absolute_url reference:
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('link.views.category_detail', args=[str(self.name)])
html
{{ category.name }}