Django multilingual - how to preview content from any language? - django

I'm using django-multilingual for a Django based website. When I define the __unicode__ function for a model to return this way:
def __unicode__(self):
return unicode(self.title)
However my default language is English and I have some items that are inserted in Dutch only. When I preview the full list, I get "None" as a title.
Is there an easy way to try to get the English title and then check for a title in any other language just for preview?

Iterating over all translations can be easily done like this:
>>> translations = [getattr(obj, "name_" + lang[0].replace("-","_")) for lang in
settings.LANGUAGES]
Where obj is the model object, and lang will represent a tuple ('bg', 'Bulgarian') from your settings file.
lang[0].replace("-","_") is required, in case that you have languages like "uk-gb", because those values are placed into name_uk_gb

Well, assuming your model stores the Dutch version in an attribute called dutch_title, you could do this.
def __unicode__(self):
# If we've got an English title, use that
if self.title:
return self.title
# Otherwise, default to the Dutch title
return self.dutch_title
Without knowing what you mean by "some items are inserted in Dutch only", it's a bit difficult to answer your question more helpfully.

Depending on the app you are using (for example - django-multilingual), you can use:
<td>{{ object.name_en|escape }}</td>
(example from: http://code.google.com/p/django-multilingual/source/browse/trunk/testproject/templates/articles/category_list.html)
As i remember there were some other forks of this app, that used to change that behavior like this:
Objec title is: {{object.en.name}}
In case you use other app, you can always run the manage.py shell and test with dir(MyModel), or dir(MyModel.fields) to check what fields are defined there :)
In case there are magic getters for those field names and you cant see anything interesting in dir(...), you can refer to the code of your prefered l18n app and see what going under the hood :)

Related

Django: Generate form from dictionary

I'm currently working on a django-app where users have the option to select synonyms for a select number of words. These words are then replaced all over the website by these synonyms
these synonymes are defined as a separate model:
class TransportSynonyms(models.Model):
user= models.ForeignKey(user)
key = models.CharField(max_length=255)
value = models.CharField(max_length=255)
This process is done in part with template tags, and as such the number of words that can be 'synonymed' is limited. For example, the following words can be replaced by synonymes:
'train', 'plane'.
And appear like so in the html template:
{% get_trans "train" %}
{% get_trans "plane" %}
I now want to give user the ability to define synonymes for themselves, without the admin view. I created a few pages using a ListView as an overview (so the users could see which words they can change) with individual buttons that led to EditViews.
However, I'm since user have no synonyms linked to them by default, the ListView appears empty. I could solve that by passing a list from the view, but that list wont have the required '.id' values and would be worthless for linking to the EditViews.
My listview currently looks like the following table:
Original | Synonym
---------------------------
train | train (button to editview)
plane | aircraft (button to editview)
The buttons require an ID value
href="{% url 'synonym_edit' pk=synonym.id %}"
I have to find a way to fill the ListView with the 'synonymable' words and provide links to a DetailView (for synonyms might not yet exist).
I thought about dropping the ListView all together and instead pass a dictionary to a form. By default both the key and the value will be the same word
({'train' : 'train', 'plane' : 'plane'})
and a form would then be generated form this dictionary, allowing users to change the value by having the dictionary value be shown as a text input.
Its a bit more limited (and more cumbersome) than my original plan, but I think this might work. Problem is that I've only really worked with modelforms before, and I am currently stuck on having the form be generated from the dictionary. Could anyone here point me to the right direction?
Regards,
Jasper

Edit css files from django admin

My client wants to edit the css files of the site from django admin.
Is there any way to do it ? .
Basically what they want is,to be able to change the color,font etc of the data in the front end from django admin interface.
The best thing would be to just let him edit the css file itself. CSS is, in essence, a rather flexible tool, so writing a way to manage it is rather tough (and really, overkill). It's already easy to pick-up, and any nice editor like sublime or notepad++ would probably be easier and more natural than whatever you'll build using the admin site. Also, by building a simple way to control css, your client will probably start asking for more and more flexibility until you find yourself building an entire cms (trust me, I've been there myself).
What's more, your client probably only wants to manage small aspects or details of the site. Recently I had a project where I allowed my users to style their display of my application. The way I did it was to create a UserDesign model which extended the base User model and kept very specific css data. Something like this:
class UserDesign(models.Model):
user = models.OneToOneField(User)
background_color = models.CharField(max_length=15)
font_color = models.CharField(max_length=20, choices=COLORS)
theme = models.CharField(max_length=20, choices=THEMES)
Meaning, they didn't control the entirety of the css, but they did get to choose the background color and some other information. It's a very neat addition to any website. However, if you are bent over doing it the hard way, I'd do something like this:
class Selector(models.Model):
name = models.CharField(max_length=30)
def get_template(self):
attrs = [a.join() for a in self.attr_set.all()]
return """ %s { %s } """ % ( self.name, ';'.join(attrs) )
class Attr(models.Model):
key = models.CharField(max_length=30)
value = models.CharField(max_length=30)
selector = models.ForeignKey(Selector)
def join(self):
return ': '.join(self.key, self.value)
I chose 30 as the max_length completely arbitrarily (you might need it longer), and you can use a TabularInline to make each selector easy to manage. Then you can easily use different css definitions inside your templates themselves:
<style>
{% for selector in selectors %}
{{ selector.get_template }}
{% endfor %}
</style>
Of course, the Selector model would probably need another field called 'template' or 'view' or something, to link it to a certain html file, though at this point it quickly start devolving into building your own cms (which, as mentioned before, is quite a headache that not wanting to edit a text file just doesn't justify)
A third viable option is to create a view with a code-editor, and just let your client edit his css through the web page. There's more than enough client-side plugins out there, like ace or codemirror (and of course, limit that view to administrators, which very simple to do).

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.

Database localization in Django

I am using .mo files for localization in Django.
Also, in my database, I store some translated text in different fields, such as:
name_en, name_es, name_de (they are all columns in each row).
What will be the best method to choose the correct field inside a template?
i.e.:
{{ name.some_method }} will generate the correct translation based on the current localization.
Thanks,
Meit
You should look at http://goodcode.io/articles/django-multilanguage/ Here’s a simple solution that may fit your use case and is easy to implement and understand.
You should look at Django Transmeta, it work the same way as what you've done (DB fields with language code) but it's a more complete solution. It already deal with the template stuff, etc.
You can check Model Internationalization and Django Packages for more info and ideas in this domain.
I can see two method for doing this, one in your view and the other one is in the template...
In view:
Probably you keep the user language information somewhere so,
user_lang = 'es'
obj = Somemodel.objects.get(pk=123434)
obj.local_name = getattr(obj, 'name_%s'%user_lang)
So, you keep local translation in a specific variable of the instance and in your template you can use is as:
{{obj.local_name}}
But that might be costly if you wish to pass the template a queryset instead of a single instance. For a such usege you have to evaluate that value for each object in your queryset.
In template:
That is a more complex way of solving the porblem in the template...
Define a template tag and pass object_id, and local language information and get the translated text using a similar getattr function. But in that point, if you wish to use this for more than one model, you probably have to pass a content type information for your template tag too, such as:
{% get_translation <object_id> <content_type_id> <local_language> %}
And in your template tag function, do something like:
from django.contrib.contenttypes.models import ContentType
....
cont_obj = Content_type.objects.get_for_id(<cotent_type_id>) #get the related model
obj = cont_obj.get_object_for_this_type(pk=<object_id>) # get your object
return getattr(obj, 'name_%s'%<local_language>)

is there a way to use django generic views and some smart urlpatterns for quick ordering/sorting of queries?

let's assume I have a django model like this:
class Event(CommonSettings) :
author = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
event_type = models.ForeignKey(Event_Type, verbose_name="Event type")
text_field = models.TextField()
flag_box = models.BooleanField()
time = models.TimeField()
date = models.DateField()
project = models.ForeignKey(Project)
now, by default, I have a view where I sort all events by time & date:
event_list = Event.objects.filter().order_by('-date', '-time')
however, maybe the user wants to sort the events by time only, or by the date, or maybe in ascending order instead of descending. I know that I can create urlpatterns that match all these cases and then pass on the these options to my view, however I feel like I'm reinventing the wheel here. the django admin site can do all of this out of the box.
So here's my question: is there a clever, easy way of getting this done in a generic way, or do I have to hard code this for my models / views / templates?
and yes, I did find solutions like this (https://gist.github.com/386835) but this means you use three different projects to achieve one thing - this seems to be a too complicated solution for such a simple thing.
EDIT1:
how do I have to change the template so that I can combine multiple filters? Right now I have
Desc
Asc
but I want to allow the user to also change number of entries that get displayed. So I have:
order by date
order by name
This works all fine, but if I click on 'order by date' and then I click on 'Asc', then my previously selected order disappears. That's not what I want. I want the user to be able to combine some options but not others.
EDIT2:
unfortunately your solution doesn't work with
from django.views.generic.list_detail import object_list
and it's
paginate_by
option.
I tried:
prev
{% trans "next" %}
but the links then just don't work (nothing happens). maybe you need to do something special with "object_list"?
I don't think it's as much work as you're making it out to be - you can use variables instead of explicitly creating separate url patterns. If you look at how the django admin handles it, they tack on request variables to the url like ?ot=asc&o=2 This corresponds to sort in ascending order in by the 2nd column. Of course, if you designing a particular page, you might as well use more readable naming. So instead of numbering the categories, i'd do ?sort=desc&order_by=date and then put a regular expression in the view to match the different possibilities. Something like:
order = re.match(r"(?:date|time|name)$", request.GET['order_by'])
if request.GET['sort'] == 'desc':
order = '-' + order
results = Event.objects.filter().order_by(order)
You could instead use the regexp as a url pattern matcher as you suggested, but it's more common to let the url itself represent which part of the site you're at (i.e. website.com/events/) and the url request variables represent how that content is being displayed (i.e. ?order_by=date&sort=desc).
Hope that helps!
EDIT: For the second part of your question, use Django's templating system (which reads variables) instead of just html. There are several ways I can think of to do this, depending on personal preference and how exactly you want the UI to function (i.e. page loads with new variables anytime the user chooses a filter, or the user chooses all filter options in a form and then submits it so the page only has to reload once, etc). In this case, you could just do:
Ascending
Descending
Name
Date
Then in the view make sure your render_to_response arguments include a dictionary that looks like: {'order': request.GET['order_by'], 'sort': request.GET['sort_by'], }
Unfortunately, (and someone please correct me if I'm wrong) I don't think there's a template tag to generate a url with request.GET parameters - the url tag {% url name_of_view order_by=name sort_by=desc %} would generate "path/to/name_of_view/name/desc/", but I don't think there's a tag to generate "path/to/name_of_view?order_by=name&sort_by=desc". It would be pretty easy to write a custom tag for this though (I wouldn't be surprised if there's already one on django-snippets or something, although I just did a quick google search and didn't find anything).