Django: a custom template tag to convert links inside of a TextField and change the hyperlink text - django

The scenario is that there are some dynamic texts on some templates, that will contain hyperlinks.
For this, I have a SiteDataKeyValue model, in which the dynamic texts for different parts of the template are inputted. This is the model:
class SiteDataKeyValue(models.Model):
key = models.CharField(
max_length=200, verbose_name="نام متن مورد نظر", unique=True
)
value = models.TextField(verbose_name="متن")
def __str__(self):
return self.key
A solution that I've found already, is Django urlize template tag. As mentioned in the docs, this tag converts texts like https://www.google.com to www.google.com, which is nice but not what I'd like to achieve. I want to be able to change the hyperlink text, so the output would be something like: Click Here!.
I searched for a bit, came across modules like bleach, which is a fine module, but I couldn't find the answer I was looking for (I skimmed through the docs and there was nothing about the hyperlink text).
Also I saw a comment somewhere telling that this could be achieved by writing a custom Django template tag, but although I tried to do this regarding the custom template filters docs, I didn't have a clue to how to achieve this.
I'm not asking for the code, although it would be really appreciated if you provide instructions for writing this custom template tag, or better, if you could point me to something like this that is already out there.

First of all you can extend urlize tag like the answer in this
or you can change the main code which you can find it in django.utils.html and override its url variable to change it.
But I think the best method is extending the urlize tag
like this:
{% text | urlize | change_a_text_filter:{{ dome_new_a_text }} %}
then you can scrape the text and use regex to find >sample-text</a> then you can change it to the argument that defines in your tag
from django import template
register = template.Library()
#register.simple_tag
def change_a_text_filter(format_string, arg):
# find the url that made in urlize with regex
# change it with arg
# return the result

I was on a completely wrong road to solve this problem. I was trying to urlize a link from TextField, but didn't consider the fact that I only needed to implement html code as Visit link.com! in the TextField, and then use safe template tag to render html directly as below:
{{ text.value|safe }}
So in this solution, there is no need to urlize, and of course there is no need to extend this tag neither.
NOTE: As commented by #rahimz (link to comment) I understand that there are safety concerns regarding safe tag, So I should emphasize that only me and a company-trusted admin will have access to admin panel and there is no worries that this admin will send malicious code through this TextField.

Related

how to remove html tags in django template on browser while showing to user

As shown in figure i used {{options|safe}} for rendering options in my django 3.0 polls application even though it is rendering like that and i don't know how to remove the tags from rendered string, thanks for help in advance
regarding tag error
To remove tags, I would recommend using Mozilla's bleach library.
In order to remove tags only in the front-end, not the data itself, you can easily create a custom template filter and clean the tags inside it.
Another cool idea would be to have list of enabled HTML tags that can be used (like making text bold with <b>...</b>) and then render the input as a valid html:
{{ options|remove_tags|safe }}
Example for a custom template filter:
#register.filter
def remove_tags(value):
return bleach.clean(value, tags=["b", "i"])

Detect URLs and #tags in Django CharField and add style to it

For now on I have in my template a paragraph like this <p class="...">{{ post.content }}</p> and if this Post's content contains a link or #hashtag it is rendered as a normal text with the rest of the post. How can I customize it? For example change text-color and add tag around it?
As I said in comment, you can use custom tag filter to wrap your content, and use Regular Expression to generate links and hashtags
Create your tags file, and name it as you want:
tag_filter_name.py
If you're not familiar with custom tag filter creation, you can learn more about it in the Official Documentation
from django import template
import re
register = template.Library()
def generate_link(link):
return '<a class="link" href="{}">{}</a>'.format(link, link)
def generate_hashtag_link(tag):
# Free to configuree the URL the way adapted your project
url = "/tags/{}/".format(tag)
return '<a class="hashtag" href="{}">#{}</a>'.format(url, tag)
And then, you create the function what will be used as tag filter
#register.filter
def render_content(obj):
text = re.sub(r"#(\w+)", lambda m: generate_hashtag_link(m.group(1)),obj)
return re.sub(r"(?P<url>https?://[^\s]+)", lambda m: generate_link(m.group(1)),text)
If you want Django to mark it as safe content, you can do the following:
from django.utils.safestring import mark_safe # import function
''' function codes here '''
return mark_safe(re.sub(r"(?Phttps?://[^\s]+)",
lambda m: generate_link(m.group(1)),text))
And finally, to use it in your template, don't forget to load it
{% load tag_filter_name %}
{{ post.content|render_content }}
Best way: custom tag filters here is the docs URL
https://docs.djangoproject.com/en/2.2/ref/templates/builtins/
Good way: If you know JS create a function that handles the formatting on the front end
in HTML:
onload="myfunction("{{post.content}}")"
in JS sort for the string containing the # wrap it in a span or other element and style away. then replace the inner HTML with your new formatted piece. This will save rendering time on the server and also frees you up from having to loop thru the list of posts in the view
Ok way: not preferred but if you hate js and only want to work in python (understandable). You need to loop through the list of posts separate out the items of the post format them the way you like with inline style. then add them to a new object that you will append to the end of a new list of posts that you will then pass thru to context. This is a real pain please don't do this if you can help it at all.
the tag filters are awsome take advantage but if they won't work for your use case I would highly advise using vanilla JS

Django-Haystack: Understanding the use of data template in Haystack

I am new to Haystack. I cannot understand why we have to use a template to render it with the text that we want to search. More simple , why we don't have to use something like this?
text = indexes.CharField(document=True, "and here the attributes to search")
UPDATE
To be more specific Let's say that we have an app places an here a model countries.
In the model i want to be searchable from haystack the fields capital and biggest_cities. So in search_indexes.py i put
text = indexes.CharField(document=True, use_template=True )
After make a template in the path search/indexes/places/countries_text.txt
Here i put
{{ object.capital }}
{{ object.biggest_cites }}
Again the question is: why we have to use a template in order to accomplish our goal?
It wouldn't be easier to use something like
text = indexes.CharField(document=Truer, model_attr='capital',model_attr='biggest_cites')
Have you read this Haystack Documentation page http://django-haystack.readthedocs.org/en/latest/searchindex_api.html ?
If you haven't, you must. If you have, read it again.
The SearchIndex API contains valuable fundamentals of how Haystack works on your project. It can also grant you a useful insight of "why you use templates to make your data searchable'.
why we have to use a template in order to accomplish our goal?
From the Haystack Docs:
"...we’re providing use_template=True on the text field. This allows us to use a data template (rather than error prone concatenation) to build the document the search engine will use in searching"
As you can see, we can choose whether use a template or not.
Ps: sorry for the late post; I hope it helps you.

Customizing Django.contrib.comments honeypot

I'm using Django's standard comment system and I would like to extend its anti-spam honeypot capability.
I thought of changing the default "name" and "id" of the field to something more alluring for spam-bots such as "website". I checked the html and this looks like this:
<p style="display:none;">
<label for="id_honeypot">Never send a human to do a machine's job</label>
<input type="text" name="honeypot" id="id_honeypot" />
</p>
Am I correct in thinking that changing the defaults of this element would boost its anti-spam capabilities? I tried modifying it in the django/contrib/comments/forms.py like this:
class CommentForm(CommentDetailsForm):
#use to be honeypot = forms.CharField(...
website = forms.CharField(required=False,
label=_('Never send a human to do a machines job')
def clean_honeypot(self):
"""Check that nothing's been entered into the honeypot."""
value = self.cleaned_data["website"]
if value:
raise forms.ValidationError(self.fields["website"].label)
return value
And this successfully changes the name and id in the html generated by django BUT then the whole mechanism stops working - I tried populating this invisible field, submitted and the comment was added.
I have a few other ideas as well, but first I'd really like to get this working - is it possible to modify the default honeypot name and id AND have it working like it should?
P.S I believe a more elegent way of doing this would be to extend django.contrib.comments and code the modification there instead of working on actual django code - what would be the best way of accomplishing this?
Given a bit more time to tinker around I found the answer to both of my questions:
In order to modify the standard honeypot or to create your own, you have to extend the CommentForm class by adding a clean_NAME_OF_HONEYPOT function as well as a NAME_OF_HONEYPOT variable both of which look similar to the standard ones and you also have to override the security_errors function to include the name of your new/modified honeypot in the dictionary.
The best way to do this is to create your custom comments app as described here: https://docs.djangoproject.com/en/dev/ref/contrib/comments/custom/ .
I hope this answer helps anyone else in my situation.

Putting links in list_detail.object_list to list_detail.object_detail

I've started using Django and am going right to generic views. Great architecture! Well, the documents are great, but for the absolute beginner it is a bit like unix docs, where they make the most sense when you already know what you're doing. I've looked about and cannot find this specifically, which is, how do you set up an object_list template so that you can click on an entry in the rendered screen and get the object_detail?
The following is working. The reason I'm asking is to see if I am taking a reasonable route or is there some better, more Djangoish way to do this?
I've got a model which has a unicode defined so that I can identify my database entries in a human readable form. I want to click on a link in the object_list generated page to get to the object_detail page. I understand that a good way to do this is to create a system where the url for the detail looks like http://www.example.com/xxx/5/ which would call up the detail page for row 5 in the database. So, I just came up with the following, and my question is am I on the right track?
I made a template page for the list view that contains the following:
<ul>
{% for aninpatient in object_list %}
<li><a href='/inpatient-detail/{{ aninpatient.id }}/'>{{ aninpatient }}</a></li>
{% endfor %}
</ul>
Here, object_list comes from the list_detail.object_list generic view. The for loop steps through the object list object_list. In each line I create an anchor in html that references the desired href, "/inpatient-detail/nn/", where nn is the id field of each of the rows in the database table. The displayed link is the unicode string which is therefore a clickable link. I've set up templates and this works just fine.
So, am I going in the right direction? It looks like it will be straightforward to extend this to be able to put edit and delete links in the template as well.
Is there a generic view that takes advantage of the model to create the detail page? I used ModelForm helper from django.forms to make the form object, which was great for creating the input form (with automatic validation! wow that was cool!), so is there something like that for creating the detail view page?
Steve
If you're on django < 1.3 then what you are doing is basically perfect. Those generic views are quite good for quickly creating pages. If you're on django 1.3 you'll want to use the class based generic views. Once you get a handle on those they are are crazy good.
Only note I have is that you should use {% url %} tags in your templates instead of hardcoding urls. In your urls.conf file(s) define named urls like:
url('inpatient-detail/(?P<inpatient_id>\d+)/$', 'your_view', name='inpatient_detail')
and in your template (for django < 1.3):
...
In 1.3 a new url tag is available that improves life even more.