Thread: [SQLObject] Objects with localized properties
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: deelan <de...@in...> - 2003-05-07 09:34:29
|
hi there, i'm new to sqlobject and python in general, i'm developing using .net framework but i'm dissatisfied of the system for a number of reasons, one of them is the lack of proper ORM tool. microsoft has objectspace under development but the current "tech preview" it's just a demo, e.g. you can't do many-to-many relations. so i'm thinking to switch to python, webware, cheetah and sqlobject. i've read the docs and i was wondering how can i solve some tipical challenges i'm having while i develop web app with .net. it happens quite often to have to deal with some business objects that have multiple strings (names, descriptions, etc.) to help app localization. just think about a product in a catalogue that needs two, three descriptions based on the locale of the user browsing the page. how would you model the object? if you retrieve the object from the db with: p1 = Product(10) the object would contain all the possibile names in the implemented locales? what about a: p1 = Product(10, 'it') p1.name <-- get italian localized name to load up just the italian name and description for the product? i think this would case some issues, since if i do: p2 = Product(10, 'en') p2 is really the same object in the DB but now contains english name and description. probably this solution make more sense: p1 = Product(10) p1.setLocale('it') p1.name <-- get italian name p1.setLocale('en') p1.name <-- get english name finally i would like to avoid doing this: p1 = Product(10) p1.nameEn <-- return english name p1.nameIt <-- return italian name since when a new locale is added i need to add a new variable. what are your thoughts? thanks is advance. |
From: Ian B. <ia...@co...> - 2003-05-07 13:28:11
|
On Wed, 2003-05-07 at 04:34, deelan wrote: > p1 = Product(10) > > the object would contain all the possibile names in the implemented > locales? what about a: > > p1 = Product(10, 'it') > p1.name <-- get italian localized name > > to load up just the italian name and description for the product? i > think this would case some issues, since if i do: > > p2 = Product(10, 'en') > > p2 is really the same object in the DB but now contains english name and > description. probably this solution make more sense: > > p1 = Product(10) > p1.setLocale('it') > p1.name <-- get italian name > p1.setLocale('en') > p1.name <-- get english name > > finally i would like to avoid doing this: > > p1 = Product(10) > p1.nameEn <-- return english name > p1.nameIt <-- return italian name > > since when a new locale is added i need to add a new variable. > what are your thoughts? What are you thinking for the database schema? I imagine there's two columns in that case. One possibilty might be to have a nameEn and nameIt, and do to: class Product(SQLObject): ... def _get_name(self): if self.locale == 'en': return self.nameEn elif self.locale == 'it': return self.nameIt def _set_name(self, value): if self.locale == 'en': self.nameEn = value elif self.locale == 'it': self.nameIt = value But, assuming locale gets set by the interface somehow, this makes Person unthreadsafe for multiple users with multiple languages (since typically a single object would be shared among them). Though if you turn caching off there's no reason not to share objects among threads (though you can't do that now). You could also do p1.name('en'), which is probably marginally better than p1.nameEn. You end up programming name() on your own though, which would be tedious though straightforward. Essentially it is a compound field (i.e., a single Python field that has multiple database fields) -- a similar problem perhaps to something like a point object, where the point is made up of an x and y column. Then you might get p1.name['en'] -- i.e., name returns a dictionary, that is created from name_en, name_it, etc. Maybe that would be defined like: class Product(SQLObject): name = DictOfCols({'it': 'name_it', 'en': 'name_en'}) But that's just speculative. Again, without changes you can implement _get_name(self): return {'it': self.nameIt, 'en': self.nameEn}, at least until I feel clearer implementing something specific. Maybe Person(1, 'it') would be possible, at some point, but I'm not sure how I would implement that now. Ian |
From: deelan <de...@in...> - 2003-05-07 17:34:45
|
Ian Bicking wrote: >>probably this solution make more sense: >> >>p1 = Product(10) >>p1.setLocale('it') >>p1.name <-- get italian name >>p1.setLocale('en') >>p1.name <-- get english name >> >>finally i would like to avoid doing this: >> >>p1 = Product(10) >>p1.nameEn <-- return english name >>p1.nameIt <-- return italian name >> >>since when a new locale is added i need to add a new variable. >>what are your thoughts? > > > What are you thinking for the database schema? I imagine there's two > columns in that case. to clarify as DB schema i was thinking to something like this: Product table ProductId ... ProductName table ProductId Name Lang where "lang" is out 2 char lang code, this would require a join between the two tables, by doing this i will avoid to mess up things when i need to add a name with a new localization. if i understood correctly via sqlobject i can auto join the two tables. i need to use one of those Join objects, is this correct? > > One possibilty might be to have a nameEn and nameIt, and do to: > > class Product(SQLObject): > ... > def _get_name(self): > if self.locale == 'en': return self.nameEn > elif self.locale == 'it': return self.nameIt > def _set_name(self, value): > if self.locale == 'en': self.nameEn = value > elif self.locale == 'it': self.nameIt = value > > But, assuming locale gets set by the interface somehow, this makes > Person unthreadsafe for multiple users with multiple languages (since > typically a single object would be shared among them). mmm, that's true. i didn't think to this possibility. > (...) Then you might get p1.name['en'] -- i.e., name returns a dictionary, that is > created from name_en, name_it, etc. Maybe that would be defined like: > > class Product(SQLObject): > name = DictOfCols({'it': 'name_it', 'en': 'name_en'}) > > But that's just speculative. Again, without changes you can implement > _get_name(self): return {'it': self.nameIt, 'en': self.nameEn}, at least > until I feel clearer implementing something specific. mmm, this is a nice solution but i'm having hard time to figuring out the entire thing. i'll think about it. thanks for the support. |
From: Ian B. <ia...@co...> - 2003-05-07 18:00:39
|
On Wed, 2003-05-07 at 12:38, deelan wrote: > to clarify as DB schema i was thinking to something > like this: > > Product table > ProductId > ... > > ProductName table > ProductId > Name > Lang > > where "lang" is out 2 char lang code, this would require > a join between the two tables, by doing this i will avoid to > mess up things when i need to add a name with a new > localization. if i understood correctly via sqlobject i can > auto join the two tables. i need to use one of those Join > objects, is this correct? Yes, that's probably an easier layout too. In this case a join doesn't seem quite right... hmmm... you might do: class Product(SQLObject): _joins = [MultipleJoin('ProductName')] def name(self, lang): return ProductName.selectBy( productID=self.id, lang=lang)[0].name class ProductName(SQLObject): productID = KeyCol(foreignKey='Product') name = StringCol() lang = StringCol(length=2, varchar=False, notNone=True) Some might say that ProductName doesn't need a primary key (productID + lang is unique, and a compound key). My response is always: then it should be a join. Maybe that's true in this case, i.e., some new LangJoin(...). I'll think about this. Creating a specific join of this type is still mostly theoretical -- I haven't actually done one, and I don't know if anyone else has either. Ian |
From: Luke O. <lu...@me...> - 2003-05-07 20:35:23
|
> Yes, that's probably an easier layout too. In this case a join > doesn't > seem quite right... hmmm... you might do: > > class Product(SQLObject): > _joins = [MultipleJoin('ProductName')] > > def name(self, lang): > return ProductName.selectBy( > productID=self.id, lang=lang)[0].name This leads me to a vague desire I've had for a while: to be able to do selects over joins (really, a filter). Conceptually, something equivalent to: def name(self, lang): return [ x for x in self.productNames if x.lang == lang ][0] although I'd like a cleaner way to add the condition. def name(self, lang): return self.productNamesWhere(q.lang == lang)[0] except this gets into the mess (which i don't entirely mind) of the SelectResults system... Just some thoughts for down the road. I find my self wanting to filter joins on a regular basis, and end up doing conditional list comprehensions like my first example (sql equiv 'where publish <> 0', etc), but wonder if its worth working it into the core. - Luke |
From: Ian B. <ia...@co...> - 2003-05-07 21:56:37
|
On Wed, 2003-05-07 at 15:20, Luke Opperman wrote: > This leads me to a vague desire I've had for a while: to be able to do > selects over joins (really, a filter). Conceptually, something > equivalent to: > > def name(self, lang): > return [ x for x in self.productNames if x.lang == lang ][0] > > although I'd like a cleaner way to add the condition. > > def name(self, lang): > return self.productNamesWhere(q.lang == lang)[0] > > except this gets into the mess (which i don't entirely mind) of the > SelectResults system... I think I understand, and it shouldn't be too hard. I want to make joins a bit more powerful anyway (in terms of adding methods to the class). Can you give a few more use cases? That would make it easier for me to figure out the interface. Ian |