New hooks in ModelBase, lookup_inner and Options

3 views
Skip to first unread message

marcin.k...@gmail.com

unread,
Feb 22, 2007, 6:55:27 AM2/22/07
to Django developers
Hi,

I am impressed in what I could do for django-multilingual without any
modifications to Django, but still there were some places I needed to
change its behavior and had no other choice than to assign new
versions of some functions and methods. Very much in the spirit of
AOP, I guess, but perhaps it would be better to have a cleaner way to
do it.

I am willing to create the patches, but I need some suggestions from
people who know the Django code better.

1. D-M needs to modify the definition of a translatable model before
ModelBase creates the class. This one should be easy, ModelBase could
send a pre_init signal right at the beginning of __new__.

2. D-M allows filtering by the values of translated field and I had to
override lookup_inner to make it possible. My function handles only
those parts of lookups that refer to translated fields and calls the
original lookup_inner for all the rest. I am not really sure what
would be the clean way to do this; signals are good for all the
initialization code, but adding them in a function used this often in
request handling might kill performance. What do you think?

It might be possible that assigning to models.query.lookup_inner
actually is the best way to do it.

3. I needed to change RelatedObject.get_list for translation models so
that it returns a list with the right ordering. It would be nice to
be able to use a subclass of RelatedObject, but the name RelatedObject
is hardcoded in class Options. How about adding a
get_related_object_class() method to the ForeignKey class? Then you
could do something like this:

class MyForeignKey(models.ForeignKey):
def get_related_object_class(self):
return MyRelatedObject

class MyModel(models.Model):
some_fk = MyForeignKey(SomeOtherModel)

-mk

Malcolm Tredinnick

unread,
Feb 22, 2007, 6:17:53 PM2/22/07
to django-d...@googlegroups.com
Unfortunately, I have more questions than answers here. Maybe you will
be able to clear up a few points so that we/I can see what you are
asking.

On Thu, 2007-02-22 at 03:55 -0800, marcin.k...@gmail.com wrote:
[...]


> 1. D-M needs to modify the definition of a translatable model before
> ModelBase creates the class. This one should be easy, ModelBase could
> send a pre_init signal right at the beginning of __new__.

What is D-M? Is this your modified version of the code?

What is a translatable model? That sort of concept doesn't seem to
already exist in Django; translation happens on a per-string basis, not
at any higher level. So I'm not really sure what this new signal would
do (I would be very reluctant to add any extra signals to that part of
the code, since it's a bit performance critical and signals are not
free, so it's going to need a good use case and no alternative to
convince me, at least).

> 2. D-M allows filtering by the values of translated field and I had to
> override lookup_inner to make it possible.

Again, I don't understand this. Do you mean filtering by the translated
field name? Or by the translated contents (which come out of the
database)?

[...]


> 3. I needed to change RelatedObject.get_list for translation models so
> that it returns a list with the right ordering.

Again, I'm having trouble working out what might be the real problem
here, because I don't understand what you are trying to do. Could you
provide an example of the sort of thing you are trying to do with these
modifications? It's a little hard to think about this area of the code
completely abstractly. Again, I would think that adding all sorts of
sub-classing hooks in that part of the code is probably not the first
thing I would think of, for performance and maintainability reasons, but
it's hard to suggest anything else on the information we have at the
moment.

Regards,
Malcolm

marcin.k...@gmail.com

unread,
Feb 23, 2007, 4:31:18 AM2/23/07
to Django developers
Hi Malcolm,

On 23 Lut, 00:17, Malcolm Tredinnick <malc...@pointy-stick.com> wrote:
> Unfortunately, I have more questions than answers here. Maybe you will
> be able to clear up a few points so that we/I can see what you are
> asking.

Sure. I thought I would just describe the new functionality I would
need from Django without going into the details of my library, but
perhaps I should have added more details.

Django-multilingual is a library I'm building to create multilingual
models. Such a model contains one or more fields with values
depending on language. There is no documentation yet, but my
introduction and a discussion in django-users should give you an idea
where it goes:

http://blog.elksoft.pl/index.php/introduction-to-django-multilingual/
http://groups.google.com/group/django-users/browse_frm/thread/6a96426735ffd7f1

In short, it lets you create a model like this:

class Category(models.Model):
# First, some fields that do not need translations
creator = models.ForeignKey(User, verbose_name=_("Created by"),
blank=True, null=True)
parent = models.ForeignKey('self', verbose_name=_("Parent
category"),
blank=True, null=True)

# And now the translatable fields
class Translation(multilingual.Translation):
name = models.CharField(verbose_name=_("The name"),
blank=True, null=False, maxlength=250)
description = models.TextField(verbose_name=_("The
description"),
blank=True, null=False)

This will create additional CategoryTranslation model with all fields
from Translation, a ForeignKey to the Category model and language
information. Then you can do things like:

# create a category with
c = Category(name_en='category 2', name_pl = 'kategoria 2')
c.save()

Category.objects.all().filter(name_en__contains='2')
Category.objects.all().filter(name_pl__contains='2')
Category.objects.all().filter(name__contains='2')

The last one means "filter by name in the language I chose as default
while handling this request".

This is actually the main idea behind the library: tell it which
fields in your model should have values depending on language and then
simply use them much like they would be 'normal' fields, but with some
added functionality, like the 'name' field that maps to 'name_en' or
'name_pl' depending on the language you chose as the 'default'. The
list of languages is configurable in settings, so adding another
language to all multilingual models means only one change in your
settings file.

There is more functionality to the library, like ordering and playing
nice with the admin interface. You can see other API examples in my
unit tests:

http://django-multilingual.googlecode.com/svn/trunk/testproject/articles/models.py

> On Thu, 2007-02-22 at 03:55 -0800, marcin.kaszyn...@gmail.com wrote:
> > 1. D-M needs to modify the definition of a translatable model before
> > ModelBase creates the class. This one should be easy, ModelBase could
> > send a pre_init signal right at the beginning of __new__.
>
> What is D-M? Is this your modified version of the code?

Nope, it is the library -- django-multilingual. Not very creative, I
know.

It does not require any modifications of Django source code, so you
can simply add it to your list of installed apps and it will just
work. Well, with one exception, ordering by translatable columns in
admin list view requires patch from #3397, I hope this will get into
trunk soon :) But the library does work without it.

Some if the functionality meant extending Django a bit, though, and
since I wanted the library to work with vanilla trunk I had to do a
couple of hacks that could be replaced with a number of small and
generic patches to Django.

> What is a translatable model? That sort of concept doesn't seem to
> already exist in Django

Working on it :)

> So I'm not really sure what this new signal would
> do (I would be very reluctant to add any extra signals to that part of
> the code, since it's a bit performance critical and signals are not
> free, so it's going to need a good use case and no alternative to
> convince me, at least).

I meant sending a pre_init signal in ModelBase.__new__ that would
allow external code to make modifications to attrs before the model
class gets gets created.

> > 2. D-M allows filtering by the values of translated field and I had to
> > override lookup_inner to make it possible.
>
> Again, I don't understand this. Do you mean filtering by the translated
> field name? Or by the translated contents (which come out of the
> database)?

I meant the latter, like in the filtering examples above. Filtering
by name_en and name meant adding some logic to lookup_inner; I agree I
might have asked this question too soon, with too little information.

I guess it should be enough to know if there are any plans to change
the interface of lookup_inner; if not, then it is highly probable that
my way of doing things (assigning a new value to query.lookup_inner
and calling the old function for all the normal stuff) will be
compatible with future releases.

> > 3. I needed to change RelatedObject.get_list for translation models so
> > that it returns a list with the right ordering.
>
> Again, I'm having trouble working out what might be the real problem
> here, because I don't understand what you are trying to do.

I am trying to do two things here:

1. provide an interface for editing model translations in admin. See
here:

http://blog.elksoft.pl/wp-content/uploads/2007/01/admin_multilingual_category.png

The obvious idea is to use the inline editor machinery, but I want to
enforce the order of translations. It is not enough to order them
when they are loaded from the database, because some translations
might not be present (ie. if the database does not contain English
version of CategoryTranslation I still want to have the editor for
English above the editor for Polish).

This means reordering either the list of instances returned by
RelatedObject.get_list or the list of form_field_collection_wrappers
in my subclass of admin_modify.StackedBoundRelatedObject. I started
with the latter, but the former makes it much easier to do the other
thing:

2. provide an easy way to edit translatable fields in your own forms

Ideally you could just write {{ form.name_en }} or {{ form.name }},
but I want to start with a custom tag first. Something like {%
edit_translation form name en %}. But an efficient implementation of
this tag needs the same instance ordering as the admin interface, so
RelatedObject.get_list seemed to be the right place to do it.

I don't know if that was too little or already too much
information :) I will be happy to provide more if necessary.

-mk

Reply all
Reply to author
Forward
0 new messages