Django-haystack - how to represent relations in search indexes? - python

So I'm trying to index some items with Django-Haystack (elasticsearch backend), one of the indexing criteria being tags on the item, which are a m2m relation(I implemented my own custom solution as it was easier for me than using taggit), here is what my models look like.
class GalleryTag(models.Model):
tag = models.CharField(max_length=100, unique=True)
slug = AutoSlugField(populate_from='tag', unique=True)
class Meta:
abstract = True
def __unicode__(self):
return self.tag
class Tag(GalleryTag):
pass
class Artist(GalleryTag):
pass
class Character(GalleryTag):
pass
class Gallery(models.Model):
characters = models.ManyToManyField(Character, blank=True, related_name='characters')
artists = models.ManyToManyField(Artist, blank=True, related_name='artists')
tags = models.ManyToManyField(Tag, blank=True, related_name='tags')
def __unicode__(self):
return self.name
The object I'm trying to index to be searchable is Gallery, and I would like to be able to have the tags, artists, and characters(all the m2ms) be one of the searchable criteria on them. I could not really find anything about how to make relations searchable, the basic examples only use completely flat models. Thanks.

One way to do this would be to pull in the data in the template file of GalleryIndex.
Something like:
{% for s in object.hasTags.all %}
{{t.tag}}
{% endfor %}
If, for whatever reason, resolving your relation is too complex for a template then add a field called tags to GalleryIndex and add a routine prepare_tags(self, obj) that queries the relevant data, concatenates and returns it as a string.

Related

Django - Retrieve or get data from selected foreign key field

Relatively new to Django, I'm working on a Django project and attempting to retrieve particular foreign key object into variable when it's selected in Form.
model.py
class item_category(models.Model):
idItemCat = models.CharField(primary_key=True max_length=5)
nameCategory = models.CharField(max_length=150)
def __str__(self):
return self.nameCategory
class item_code(models.Model):
idItemCat = models.ForeignKey(item_category, on_delete=models.DO_NOTHING)
idItemCode = models.CharField(primary_key=True, editable=False, max_length=20)
def __str__(self):
return self.idItemCode
I know I could retrieve object with making QuerySet such as .objects.last() and .objects.filter() or else, but it's just retrieve objects from database or existing data. What I'm about to do is, when a user submit a new data it'll retrieve particular foreign key object based on what I'm selected in this Form, so I could put into variable.
Any idea how should do it? it would be so much appreciated.
You really should look at the Django Docs on forms. They are excellent, and will teach you the right way to handle forms, a complicated topic.
To answer your question directly, it looks like you already have the html part, and as long as it has the form tag, like this:
<form action='your_view' method='post'>
...
</form>
Then in your view you could do something like this:
def your_view(request):
if request.method == 'POST':
category = item_category.objects.get(pk=request.POST.get('name_attribute_of_your_input'))
I'd need to see more specifics to give you a better answer, but there are several issues you should fix first.
First, your class names should be capitalized, field names lower case, and second,are you sure you want to make a CharField the primary key? You can, but for most cases, the automatically generated integer pk that Django creates is best.
class ItemCategory(models.Model):
# Django will create an integer pk for you
# idItemCat = models.CharField(primary_key=True max_length=5)
nameCategory = models.CharField(max_length=150)
def __str__(self):
return self.nameCategory
class ItemCode(models.Model):
idItemCat = models.ForeignKey(ItemCategory, on_delete=models.DO_NOTHING)
# Again, just let Django generate the primary key
# idItemCode = models.CharField(primary_key=True, editable=False, max_length=20)
def __str__(self):
return self.idItemCode

Substract a substring from a string in a model object in Django

I'm building a blog website with Django, and for posts on the blog I want to have 2 pages, first page would be the page where all the posts are displayed as a list, and the second would be a page for a specific post, with all the details. On the first page I want to show every post with title, date, and a part of the post's text. I thought that I can add to the post model another field which'll hold a substring from the hole post's text. My post model is this:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=500)
content = models.TextField()
display_content = models.TextField(blank=True)
tags = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
I want for every post the display_content field to hold the first 167 characters from the content field + "...", and I'm not sure that I'd have to implement something directly on the class Post, maybe a function, or if I need to make this operation on the view function that renders this page with posts.
You can define the logic in the __str__ method, but it might be better to define a utility function that you can later reuse:
def shorten(text, max_len=167):
if len(text) <= max_len:
return text
else:
return '{}…'.format(text[:max_len])
Note that an ellipsis character ('…') [wiki] is a single character, and normally not three inidividual dots.
Then we can use this in the __str__ method:
from django.conf import settings
class Post(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
title = models.CharField(max_length=500)
content = models.TextField()
display_content = models.TextField(blank=True)
tags = models.CharField(max_length=100)
date_posted = models.DateTimeField(auto_now_add=True)
def __str__(self):
return shorten(self.title)
For templates, Django already has a |truncatechars template filter. So if you plan to render this in a template, there is no need to implement this logic in the model. You can render the title then with:
{{ mypost.title|truncatechars:167 }}
This makes more sense, since a Django model should not be concerned with how to render data, a model deals with storing, and modifying data.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: Django's DateTimeField [Django-doc]
has a auto_now_add=… parameter [Django-doc]
to work with timestamps. This will automatically assign the current datetime
when creating the object, and mark it as non-editable (editable=False), such
that it does not appear in ModelForms by default.

django distinct in template by filter

I need to display unique values in template. I know distinct will work in query. But table in one row Many to Many field I don't know how to implement for that. If any possibilities are there to use it in template
class DoctorFeedback(models.Model):
visited_for = models.CharField(max_length=200)
tag = models.ManyToManyField(Tags)
class Tags(models.Model):
tag_title = models.CharField(max_length=50)
You can add unique=True to Tags, so there will never be any duplicates:
class Tags(models.Model):
tag_title = models.CharField(max_length=50, unique=True)

Django "for" loop by date

I'm doing blog app. I did:
{% for entry in entry.all %}
<div class="timelinestamp">
...
</div>
<br />
<br />
<br />
<br />
{% endfor %}
and almost everything works fine. I changed one Entry in my admin panel (The very first Entry...). Since then the order of my post has changed... Can anyone explain me why ? Or tell how to using loop render all Entries sorted by date ?
class Entry(models.Model):
title = models.CharField(max_length=120)
pub_date = models.DateField(null=False)
body = models.TextField()
image = models.ImageField(upload_to='images/', max_length = 100)
def __str__(self):
return self.title
The pub_date field is NOT primary key in my DB! I'm using Django 2.1
From the docs:
If a query doesn’t have an ordering specified, results are returned from the database in an unspecified order. A particular ordering is guaranteed only when ordering by a set of fields that uniquely identify each object in the results. For example, if a name field isn’t unique, ordering by it won’t guarantee objects with the same name always appear in the same order.
It looks like you want to order by pub_date? Use ordering:
class Entry(models.Model):
...
class Meta:
ordering = ['-pub_date']
If you have created model definition without the ordering meta option items in the database are not enforced any ordering, everytime when you do Model.objects.all() it will give you items without any order. If you want to queries to be in specific order you can:
Add ordering option to Meta options to model definition - which would require
database migrations
Modify your query to enforce ordering like Model.objects.all().order_by('-pub_date') - also pass the query as context object to template like:
views.py -
entries_by_pub_date = Model.objects.all().order_by('-pub_date')
context['entries_by_pub_date'] = entries_by_pub_date
template
{% for entry in entries_by_pub_date %}
...
{% enfor %}
As far as I can see you haven't defined a sort order for your Entry model. This means that you will process those entries in a non-defined order.
To order your entries you could set a default sort order on Entry:
class Entry(models.Model):
title = models.CharField(max_length=120)
pub_date = models.DateField(null=False)
body = models.TextField()
image = models.ImageField(upload_to='images/', max_length = 100)
def __str__(self):
return self.title
class Meta:
ordering = ('-pub_date',)
Or, if that's not what you're looking for, you could order your queryset in your view:
Entry.objects.all().order_by('-pub_date')

django taggit prevent overlapping tags across different models

I have two different models.
class MessageArchive(models.Model):
from_user = models.CharField(null=True, blank=True, max_length=300)
archived_time = models.DateTimeField(auto_now_add=True)
label = models.ForeignKey(MessageLabel, null=True, blank=True)
archived_by = models.ForeignKey(OrgStaff)
tags = TaggableManager()
Now say, I have defined spam, todo, urgent tags for messages.
and then I have another model:
class PersonArchive(models.Model):
from_user = models.CharField(null=True, blank=True, max_length=300)
archived_time = models.DateTimeField(auto_now_add=True)
label = models.ForeignKey(MessageLabel, null=True, blank=True)
archived_by = models.ForeignKey(OrgStaff)
tags = TaggableManager()
I define awesome, legend, rockstar for the model person. There might few more be defined.
As is quite clear, I do not want the tags for person and message to overlap.
How should I achieve that? Thanks!
You can utilize the limit_choices_to feature on ForeignKeyFields and ManyToManyFields. Your models.py file might look like this:
class PersonArchive(models.Model):
tags_field = models.ManyToManyField(Tag, related_name="people_archives", limit_choices_to={'message_archives__isnull': True})
class MessageArchive(models.Model):
tags_field = models.ManyToManyField(Tag, related_name="message_archives", limit_choices_to={'people_archives__isnull': True})
You your case, as far as I understand, you want different base families of tag for the two different models.
Take in account I'm not an expert on taggit, so the solution I'm proposing can be a bit overcomplitated, but is the first that jump me in mind by looking at the source code.
You can achieve that by extending the TaggableRel class used by TaggableManager and add a condition to the limit_choices_to parameter:
Extend TaggableRel
class CustomTaggableRel(TaggableRel):
def __init__(self, field, model_name):
super(TaggableRel, self ).__init__(field)
self.limit_choices_to = {'content_type': model_name}
Than you extend TaggableManager in the following way:
class CustomTaggableManager(TaggableManager):
def __init__(self, model_name=None, verbose_name=_("Tags"),
help_text=_("A comma-separated list of tags."), through=None, blank=False):
super(TaggableManager, self ).__init__(verbose_name, help_text, through, blank)
self.rel = CustomTaggableRel(self, model_name)
Than you your models:
class PersonArchive(models.Model):
.
.
.
tags = CustomTaggableManager(model_name="PersonArchive")
This should do the trick, didn't try the solution and I write it down very fast, but this could drive you on the right path.
Just dealt with this myself. I decided to let my tags intermingle because I found a way to only filter tags for specific models. This will only filter tags for modelname. You can expand the filter as desired.
Tag.objects.filter(taggit_taggeditem_items__content_type__model='modelname')

Categories

Resources