feincms / feincms-elephantblog

A blog for FeinCMS
feinheit.ch/labs/
BSD 3-Clause "New" or "Revised" License
43 stars 38 forks source link

Filter entries by language #15

Closed memee closed 11 years ago

memee commented 12 years ago

While adopting feincms.translations.TranslatedObjectMixin, feincms.translations.Translation to my models it's easy for me to list them and pick proper translation with object.translation and then for example object.translation.title. I always had proper translation.

How to do the same with Elephantblog? I've registered 'navigation' translations extension: I can edit translations. But when i get object_list in template it's collection of all entries disregarding language.

sbaechler commented 12 years ago

You need to register the feincms.module.extensions.translations extension to make it work.

http://feincms-elephantblog.readthedocs.org/en/latest/installation.html

saboter commented 12 years ago

Hi, i have similar problem. I integrated elephantblog with use of application content in existing feincms project. The translations extension is properly registered but i still get all entries in object_list in entry archive regardless on current language code.

simonkern commented 11 years ago

Hey, I think i have a solution for your problem ;-) I am using kind of a language switch at the root level and create seperate page trees for every language. This solution works fine for me ;-)

just edit entry_archive.html template and put sth like the following in it:

"""


{% block object_list %}
{% for entry in object_list %}
#Only use those entries that are written in the current language
{% get_current_language as LANGUAGE_CODE %} 
{% if entry.language == LANGUAGE_CODE %}           
   

{{ entry }}

{% for category in entry.fetched_categories %} {{ category }}{% if not forloop.last %},{% else %} | {% endif %} {% endfor %} {{ entry.published_on|date:"j F Y" }} {{ entry.first_image.render }} {{ entry.first_richtext.render }}
{% endif %} {% endfor %} {% endblock %}

"""

Sorry I don't know how to make github show the html tags as code :(

Best, Simon

saboter commented 11 years ago

Hello,

thanks for your advice. I fixed the problem in my fork with adding filtering by language:

https://github.com/saboter/feincms-elephantblog/commit/776a57f26d265fafe64c14d83eb364c3a9506cd6

matthiask commented 11 years ago

@saboter this solution isn't always correct. I'd recommend against adding this to the core. Most of the time the entries should be filtered by language (as you write) but I'm not sure that this is always the case.

If you need this, it's easy to add a filter to active_filters:

from elephantblog.models import Entry
from feincms.translations import short_language_code

Entry.objects.add_to_active_filters(
    lambda queryset: queryset.filter(language__startswith=short_language_code()),
    key='active_language')

Something like that should definitely be added to the documentation.

saboter commented 11 years ago

@matthiask very elegant and works like charm. Thank you. Should be definitely mentioned in docs.

sbaechler commented 11 years ago

It would actually make sense if added to the translation extension.

matthiask commented 11 years ago

@sbaechler no -- pages from feincms.module.page in other languages should not be considered inactive under all circumstances.

sbaechler commented 11 years ago

How about this:

https://github.com/sbaechler/feincms-elephantblog/commit/d49e4191ca82f37411d3d6be22df0b4dcf4f7920

matthiask commented 11 years ago

I have been thinking long and hard about this issue in the last few days, and have also discussed this offline with @sbaechler

I've added some code in my fork because I really need this feature today. I'm not happy with the solution though, I'd like to avoid stuffing knowledge about extensions inside the view code if possible.

For the moment I'd recommend using the snippet above and mark it with a big warning NOT REALLY ACTIVE/INACTIVE.

sbaechler commented 11 years ago

I think putting the language filtering in the view code is the right approach. I'm concerned about thread-safety if it is put in the manager. The problem now is finding out if the translation extension is used or not.

About your proposal: Is the queryset not executed on the transform call? Then you could save a query if you check for the language attribute on the Entry model and then define the right filter method afterwards.

I also think TRANSLATION_STRATEGY should be an attribute of the ElephantblogMixin. This way you can override it in your view.

matthiask commented 11 years ago

I just had a different idea: What if we add a method which returns a set of URL patterns for some given configuration? That way you could do something like this:

from elephantblog.urls import elephantblog_patterns

urlpatterns = elephantblog_patterns(mixin=CustomMixin, list_view_kwargs={'only_active_language': True})

(Start is here: https://github.com/matthiask/feincms-elephantblog/commit/6756b5172f5270b56c35b71f744d1fd9a38fab05)

What do you think?

sbaechler commented 11 years ago

This is better but it would mean that you have to add Translations to two places. The register extensions and the urls. More elegant would be if it was enough to just register the translation extension and that would be it.

Can we put the language filtering into an ActiveLanguageMixin? To separate it from the main view code and easier reuse.

simonkern commented 11 years ago

Hey everyone, I just played around a bit and got the following solution: I am pretty new to Django and Python so forgive me if it's really stupid :-p

I just added another EntryManager to models.py that filters Entries using the active language

class LocaleEntryManager(models.Manager, ActiveAwareContentManagerMixin):
    def get_query_set(self):
        return TransformQuerySet(self.model, using=self._db)
    def featured(self):
        return self.active().filter(is_featured=True)
LocaleEntryManager.add_to_active_filters(
    Q(is_active=True),
    key='cleared')
LocaleEntryManager.add_to_active_filters(
    lambda queryset: queryset.filter(published_on__lte=now),
    key='published_on_past')

LocaleEntryManager.add_to_active_filters(
    lambda queryset:queryset.filter(language__startswith=short_language_code()),
    key='active_language')

In the Entry class i added a further variable (look at the last line):

    class Meta:
        get_latest_by = 'published_on'
        ordering = ['-published_on']
        verbose_name = _('entry')
        verbose_name_plural = _('entries')
    objects = EntryManager()
    locale_objects = LocaleEntryManager() 

The final step is to replace the object variable with locale_object in feeds.py and views.py.

In my setup, I want to have everything included in /sitemap.xml and everything else seperated by language. This works like a charm for me now:

http://www.simme.org/sitemap.xml

http://www.simme.org/de/blog/feed

http://www.simme.org/en/blog/feed

In addition, this solution makes it pretty simple to add a few parameters to settings.py in order to decide which variable should be used for what/where.

What do you think?

Another possibility would be to check where the request comes from and then add the "active_language_filter" if necessary / wanted e.g. for the feed and the entry_archive.

sbaechler commented 11 years ago

Those are the two approaches. The issue here is that the language field is not present if the translation extension is not used. And since this is a class based view, you could define locale_objects as a mixin. And if you do a mixin anyway, you could just do the filtering there.

sbaechler commented 11 years ago

I added a test suite so it's possible to test the translation implementation once we agree on one.

I slightly modified Matthias' approach of extending the get_queryset method. But in a separate Mixin instead of the main Elephantblog Mixin.

We could discuss if it's better to simply add the Mixin to the views or dynamically add it from the URLs.

https://github.com/sbaechler/feincms-elephantblog/commit/1039c5a09e524aee500a5a0e13a0c36e54710d72

simonkern commented 11 years ago

Another factor that should be considered is the following:

When using feincms's feincms_page model, you can spit out links that lead directly to the translated page. E.g. by using:

{% feincms_languagelinks for feincms_page as links all,excludecurrent %}
{% for key, name, link, translation in links %}
   {% if link %}


{% trans "Switch language" %}: {{ key }}{% endif %} {% endfor %}

When you try to do the same with elephantblog, it gives you a wrong absolute url, because it does not take (as far as u used it) the language switch at the root level of your page into it's consideration. So you can only provide a link to the translated's blog root level, but not yet to a specific article.

For example you have a blog entry in german available at:

http://www.excample.org/de/blog/2012/10/21/test-eintrag/

It's translation link will point to:

http://www.example.org/de/blog/2012/10/21/test-entry/

Although it should be:

http://www.example.org/en/blog/2012/10/21/test-entry/

There should be a "language-switch" awareness somehow.

A simple workaround for now might be to replace the string of the actual language in the url and replace it by the language returned in the key variable.

sbaechler commented 11 years ago

It actually goes further. The page module should know in which languages the entries are available and only show those languages as options. Currently the selectable languages in the parent page are the translations of that page.

sbaechler commented 11 years ago

I have noticed that people start forking elephantblog to implement their own solution for the language problem. We should expedite finding a common solution.

simonkern commented 11 years ago

Here is a suggestion:

Since elephantblog is most likely used together with feincms, it might be a good idea to add a language parameter to feincms's Apllication Content (e.g. english, german or both - depending on available languages in settings.py). This parameter could be used to determine which entries (depending on their language) should be served under which path.

This might also solve the multiple occurences of entries in the rss-feed, when elephantblog is added to every language subtree as Application Content.

matthiask commented 11 years ago

Fixed:

https://github.com/feincms/feincms-elephantblog/blob/master/docs/installation.rst#integrating-standalone

saboter commented 11 years ago

Hi @matthiask,

posting again the same comment (deleted accidentally the old one).

Any hints how to make this new setting work with elephantblog integrated as application content? Adding this snippet didn't helped:

from elephantblog.urls import elephantblog_patterns
urlpatterns += patterns('',
    url(r'^blog/', include(elephantblog_patterns(
        list_kwargs={'only_active_language': True},
        ))),
)

Blog entries are not filtered by active language. I tried to set/hardcode the list_kwargs={'only_active_language': True} directly into elephantblog_patterns method declaration as a test. It worked after that of course.

matthiask commented 11 years ago

@saboter Where did you put this elephantblog_patterns invocation? And which file did you reference when registering the ApplicationContent?

If you put this code into myapp.blog_urls, you'll also have to use myapp.blog_urls when registering the blog app with ApplicationContent.

saboter commented 11 years ago

@matthiask you are right of course. I messed up the urls setup, shame on me. Thank you for assistance.

simonkern commented 11 years ago

as soon as i register the translation extension to Entry (as shown in the docs) I get the following error: There are duplicate field(s) in EntryAdmin.fieldsets

Traceback:
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  92.                     response = middleware_method(request)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/middleware/locale.py" in process_request
  21.         check_path = self.is_language_prefix_patterns_used()
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/middleware/locale.py" in is_language_prefix_patterns_used
  56.         for url_pattern in get_resolver(None).url_patterns:
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/core/urlresolvers.py" in url_patterns
  347.         patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/core/urlresolvers.py" in urlconf_module
  342.             self._urlconf_module = import_module(self.urlconf_name)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/utils/importlib.py" in import_module
  35.     __import__(name)
File "/srv/blog/blog/urls.py" in 
  8. admin.autodiscover()
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/contrib/admin/__init__.py" in autodiscover
  29.             import_module('%s.admin' % app)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/utils/importlib.py" in import_module
  35.     __import__(name)
File "/srv/blog/.env/src/elephantblog/elephantblog/admin.py" in 
  28. admin.site.register(models.Entry, models.EntryAdmin)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/contrib/admin/sites.py" in register
  98.                 validate(admin_class, model)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/contrib/admin/validation.py" in validate
  25.     validate_base(cls, model)
File "/srv/blog/.env/local/lib/python2.7/site-packages/django/contrib/admin/validation.py" in validate_base
  300.             raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__)
Exception Type: ImproperlyConfigured at /
Exception Value: There are duplicate field(s) in EntryAdmin.fieldsets