Mehrsprachige Modelle

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Ist es zu empfehlen ein Model so anzulegen oder sollte man das besser über eine Sprachextension lösen?

Code: Alles auswählen

class Countries(db.Model):
	id = db.Column(db.Integer, primary_key=True)
	name_de = db.Column(db.String(60), unique=True)
	name_en = db.Column(db.String(60), unique=True)
	name_fr = db.Column(db.String(60), unique=True)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Was ist wenn du weitere Sprachen hinzufügen möchtest, fügst du dann noch mehr Spalten hinzu? Was wenn du neben dem Namen noch anderen Text hast der übersetzt werden muss? Was wenn Übersetzungen fehlen, wie behält man da den Überblick? Offensichtlich ist dein Ansatz nicht sonderlich toll.

Auch darüberhinaus zeigt dein Code Probleme auf. Einer Klasse einen Namen zu geben der ein Plural ist, ist komisch. Das macht rein konzeptionell auch keinen Sinn. Wenn es dir um den Tabellennamen geht, nutz das __tablename__ Attribut.

Außerdem bist du mit den Unique Constraints auf dünnem Eis, schließlich gibt es z.b. im deutschen die Wörter Burg und Schloss die beide im Englischen Castle heißen, da wird es einfach nicht spezifischer. Ich bin mir sicher man findet auch noch weitere solche Beispiele. Überhaupt bringen Unique Constraints bei Strings die von Usern kommen nicht viel, es ist trivial zwei gleichaussehende Unicode Strings zu produzieren an dem sich der Constraint nicht stört.

Ich würde 2-3 Tabellen benutzen. Eine für die eigentliche Sache, eine für die Texte einer Sache in einer bestimmten Sprache und möglicherweise eine für die Sprachen. Auf diese Weise kannst du sehr einfach weitere Sprachen hinzufügen. Außerdem hast du eine bessere Übersicht über die Übersetzungen weil du sehen kannst welche verfügbar sind und welche noch fehlen.

Spannend ist natürlich die Frage wie genau die API aussehen sollte, hier möchte man offensichtlich nicht einfach nur mit einem relationship() Objekt hantieren. Hier muss man wahrscheinlich mal durch die SQLAlchemy Doku stöbern, es ergibt sich da aber bestimmt eine elegante Lösung.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Hi

Gemeint war das als Ländertabelle: Deutschland, Andorra, etc. da macht das Unique vermutlich Sinn? Oder gibt es überhaupt keinen Fall, wo das Unique überhaupt Sinn macht?
Musst du bei einer neuen Tabelle dann keine Spalten mehr hinzufügen? Ist das alles überhaupt über die DB zu lösen oder sollte man sich da Babel genauer angucken?

Es wäre also bei Id und Deutschen Namen zu belassen? Du meinst - wenn ich dich richtig verstanden habe - also etwas wie eine Many-to-Many Zwischentabelle? (Macht die DB dann aber dann unglaublich kompliziert.) Und wenn Übersetzungen fehlen? Das Problem scheint bestehen zu bleiben.

Den letzten Absatz habe ich nicht verstanden. Musst dich allgemeinverständlicher ausdrücken. :K
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@meego: Außer dem Namen ist nichts zu einem Country gespeichert? Die Relation wäre eine many-to-one, da man ja vom Namen eindeutig auf das Land kommen kann. Die Tabelle Countryname hätte die Spalten country_id, language_id und name. Strings sollte man, wenn es nicht wirklich einen triftigen Grund gibt, ohne Längenbeschränkung definieren.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

@Sirus3: Und das lässt sich nicht über Babel machen? Bis jetzt ist da tatsächlich nur das Land vorgesehen.

Du hast dann also eine Tabelle Country und eine Tabelle Countryname.Wobei bei Country auch irgendein name steht?
Was steht eigentlich in deinem "name" der Countrytabelle?

Bei den Beispielen waren ständig irgendwelche Beschränkungen drin (Nutzerangaben und so). Performancegründe?
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@meego: Was willst Du eigentlich machen? Für was ist die Datenbank da?
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Sirius: Kleinanzeigen.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Ich hab meine Idee mal umgesetzt, ist denke ich es ist so wesentlich verständlicher:

Code: Alles auswählen

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

from sqlalchemy import join
from sqlalchemy.orm import column_property

import bpython


app = Flask(__name__)
db = SQLAlchemy(app)


class Country(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    translations = db.relationship('CountryTranslation')


class CountryTranslation(db.Model):
    __table_args__ = (
        db.UniqueConstraint('country_id', 'lang'),
    )

    id = db.Column(db.Integer, primary_key=True)
    country_id = db.Column(db.Integer, db.ForeignKey(Country.id))
    lang = db.Column(db.String)

    name = db.Column(db.String)


class CountryTranslated(db.Model):
    __table__ = join(Country.__table__, CountryTranslation.__table__)

    country_id = column_property(Country.id, CountryTranslation.country_id)
    translation_id = column_property(CountryTranslation.id)

    country = db.relationship(Country, uselist=False)
    lang = column_property(CountryTranslation.lang)
    name = column_property(CountryTranslation.name)


if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        bpython.embed(locals_=locals())
CountryTranslated dient dazu eine angenehme API zu haben, die der im Ursprungspost nahe kommt. Das ganze kann man dann z.B. so benutzen

Code: Alles auswählen

>>> de_de = CountryTranslated(name='Deutschland', lang='de')
>>> db.session.add(de_de)
>>> db.session.commit()
>>> de_de.country.translations.append(CountryTranslation(name='Germany', lang='e
n'))
>>> db.session.commit()
>>> [(c.country_id, c.lang, c.name) for c in CountryTranslated.query.all()]
[(1, u'de', u'Deutschland'), (1, u'en', u'Germany')]
>>> Country.query.all()
[<__main__.Country object at 0x111c93d90>]
>>> CountryTranslation.query.all()
[<__main__.CountryTranslation object at 0x111c93b10>, <__main__.CountryTranslation object at 0x111c93b90>]
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Naja, ab der 3. Tabelle verstehe ich es leider nicht mehr. Insofern ist dieser Code offenbar zu komplex für mich.
Bereits das verweifacht aber die Anzahl meiner Tabellen.
Ist das überhaupt gewollt?: Ist das Französisch in der Schweiz z.B. ein anderes Französisch als das Französisch in Frankreich?
Und ist 'de' als String geeignet (sollte das nicht wieder eine Tabelle mit Kürzeln sein?: [ID, Kürzel])?
Und was mache ich mit der Übersetzung von einem Feld wie: publication_period = QuerySelectField('Veröffentlichungszeitraum', query_factory....)?
BlackJack

@meego: ISO-Länderkürzel ändern sich eigentlich, da braucht man keine zusätzliche ID für. Wo die Unterschiede zwischen schweizer Französisch und französischem Französisch liegen weiss ich nicht, aber man sollte davon ausgehen das es welche gibt. Deutsches Deutsch und östereichisches Deutsch unterscheiden sich beispielsweise bei den Monatsnamen. In de_DE heisst es „Januar” und in de_AT heisst es „Jänner“.

Die Frage ist ob Du das tatsächlich alles in die Datenbank stecken willst. Solange die Benutzer da nichts dran verändern können sollen würde ich ja einfach Babel und gettext für I18N verwenden.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Es gibt bei dem Beispiel keine dritte Tabelle. Ich mappe hier ein Model auf die Vereinigung von Country und CountryTranslation. SQLAlchemy nennt dass ein Non-Traditional Mapping. Unter der Haube macht SQLAlchemy ein JOIN über Country und CountryTranslation mit ``Country.id == CountryTranslation.id``. Mithilfe von relationship und column_property greife ich dann einfach auf die Spalten von Country und CountryTranslation zu die ich haben will.

Grundsätzlich unterscheidet man üblicherweise schon nicht nur zwischen Sprachen sondern auch Regionen. Das liegt teilweise daran dass es tatsächlich Sprachunterschiede gibt z.B. bei der USA und der UK oder Deutschland und der Schweiz, allerdings auch an unterschiedlichen Währungen, Formatierung von Zahlen, Zeit- oder Datumsangaben usw. Ich hab allerdings keine Ahnung inwieweit dies konkret Französisch betrifft.

Tatsächlich könnte man für die Sprachen selbst eine weitere Tabelle nutzen, wie ich auch in meinem ersten Beitrag im Thread angemerkt habe. Wenn man alles normalisiert haben will sollte man dies auch tun. Ich würde es aber nicht unbedingt tun, es sei den man braucht eine weitere Tabelle für irgendwelche Abfragen.

Bei dem QuerySelectField würdest du für den Query halt nur die korrekten Übersetzungen abfragen. Von meinem Beispiel ausgehend hättest du dann ``CountryTranslated.query.filter_by(lang='de')``.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

ISO-Länderkürzel ändern sich eigentlich, da braucht man keine zusätzliche ID für. Wo die Unterschiede zwischen schweizer Französisch und französischem Französisch liegen weiss ich nicht, aber man sollte davon ausgehen das es welche gibt.
Hi Blackjack: Du willst also einen ISO-Code als Länderkürzel nehmen? Was würdest du denn an den zwei ersten Tabellen konkret ändern? Welche Felder hätte es noch? Und braucht es überhaupt eine 2. Tabelle, wenn man über Babel geht?

Ich: Keep it simple and stupid? :K
BlackJack

@meego: Für eine reine Ländertabelle in verschiedenen Sprachen brauchst Du gar keine Datenbanktabelle wenn Du Babel verwendest.

Ich habe übrigens gerade mal den Unterschied zwischen französischem Französisch und schweizerischem Französisch was Ländernamen (eigentlich Gebietsnamen) angeht mit Babel herausgefunden. Viel ist es nicht, überwiegend Gross-/Kleinschreibung, aber das Länderkürzel „SX“ sticht deutlich hervor: die 'fr_CH'-Länderliste sagt dazu „Sint Maarten“ und die 'fr_FR'-Länderliste „Saint-Martin [partie néerlandaise]“.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Blackjack: Es geht um den Standort von einem Objekt. Da bietet sich eine Tabelle vermutlich schon an. Dazu vielleicht noch Bundesländer. Inwiefern man das mit Geodaten (GoogleMaps und co.) umgehen kann, weiss ich nicht?
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Eine Alternative zum Ich-Modell oben?: http://sqlalchemy-i18n.readthedocs.org/ ... start.html
Antworten