====== Strona projektu thankswithbeer.com (t) ====== [[gabriela.pasek@gmail.com|Gabriela Pasek]], [[lpiatek@gmail.com|Leszek Piątek]], [[pacho.student@gmail.com|Michał Paszko]] ==== Streszczenie ==== Projekt stanowi podstawę bazodanową dla projektu realizowanego z przedmiotu Technologie i Programowanie WWW. ==== Sformułowanie zadania projektowego ==== Celem projektu jest stworzenie serwisu, który umożliwiałby podziękowanie jego użytkownikowi piwem. Wielu internautów poszukuje w sieci pomocy, rozwiązania problemów za pośrednictwem forów czy blogów. Po uzyskaniu przydatnej odpowiedzi nasuwa się często potrzeba odwdzięczenia się danemu użytkownikowi w innej formie niż tekstowa. W życiu codziennym taką formą najczęściej jest postawienie piwa, więc dlaczego nie przenieść tego zwyczaju w sferę Internetu? Użytkownik zarejestrowany w serwisie będzie posiadał swój profil, zawierający między innymi informacje o ulubionych gatunkach piwa. Podziękowanie będzie miało formę podwyższenia 'stanu zapiwienia', który w wizualnej formie może uatrakcyjniać np. podpis na forum. Dzięki temu dany użytkownik będzie łatwo identyfikowany jako osoba, która chętnie udziela pomocy, posiada wiedzę z pewnej dziedziny, co stanowi oznakę prestiżu w społeczności internetowej. Wykorzystanie informacji z branży piwnej stwarza potencjalne możliwości zarobku związane z reklamą producentów napojów alkoholowych i ich produktów. Ponadto serwis może być sponsorowany przez firmy z branży zainteresowane współpracą. ==== Analiza stanu wyjściowego ==== Obecnie nie ma podobnych rozwiązań umożliwiających wyrażenie podziękowania za pomoc. Istnieją pluginy do darmowych forów internetowych (phpBB, invisionPowerBoard), które pozwalają na przyznanie tzw. "pomuków" dla użytkownika. Punkty te pokazują ile razy dany użytkownik okazał się pomocny. Funkcjonują jednak tylko w obrębie danego forum i są widoczne jedynie dla zarejestrowanych na nim osób, a więc wąskiej grupy Internautów. Nie ma uniwersalnego systemu punktacji, który pozwalałby na zebranie punktów z wielu serwisów i przedstawienie ich w przystępnej formie. Ponadto punkty nie stanowią atrakcyjnej formy podziękowania i w zasadzie pozostają mało zauważalne. ==== Analiza wymagań użytkownika ==== Serwis thankswithbeer.com będzie działał na zasadzie systemu z obsługą kont użytkowników - 'profilów piwoszy'. Ponadto będzie zawierał bibliotekę gatunków piwa na bieżąco uzupełnianą przez użytkowników. **Główne funkcjonalności (Must have):** * wielojęzyczność - serwis powinien posiadać kilka wersji językowych, na początek polską i angielską * możliwość dodawania i edycji biblioteki piwa - funkcjonalność powinna pozwalać na dodanie opisu produktu, zdjęcia i ceny oraz edycję tych danych * sugerowanie wpisów w bibliotece piwa - każdy użytkownik może zasugerować nowe lub uzupełnić opis / zdjęcia istniejącego piwa * moderacja - specjalne uprawnienie dla użytkownika, które będzie umożliwiało akceptowanie bądź nie sugerowanych wpisów w bibliotece piwa * zakładanie profilu piwosza - powinno pozwalać na wprowadzenie danych identyfikacyjnych (loginu i hasła, zdjęcia) oraz określenie lubianych i nielubianych gatunków piwa * możliwość generowania elementu graficznego przedstawiającego aktualny 'stan zapiwienia' do osadzania w podpisach na forach, stopkach blogów itp. * unikalny link do profilu piwosza, służący do integracji z popularnymi serwisami społecznościowymi - np. facebook, wykop.pl, grono.net, nasza-klasa.pl **Dodatkowe funkcjonalności (Could have):** * polecanie piw - użytkownicy mogą polecać sobie nawzajem produkty znajdujące się w bibliotece piwa na zasadzie wysyłania wiadomości z odnośnikiem do opisu * sugerowanie piw innych piwoszy - system może generować listę sugerowanych produktów dla użytkownika na podstawie podobieństw z profilami innych użytkowników * stawianie "mniej wirtualnego" piwa - wpłaty na konto PayPal użytkownika kwot pozwalających na zakup wybranego produktu z biblioteki piwa, % z transakcji dla portalu **Moduły:** - użytkownicy * rejestracja * logowanie * przypominanie hasła * panel ustawień użytkownika * możliwość wybrania grafik do podpisów oraz pobrania kodu do podpisu * dodawanie/edycja ulubionych piw * dodawanie/edycja nielubianych piw * moderacja bazy piw * akceptacja bądź odrzucenie wpisów * profil użytkownika * graficzne przedstawienie stanu zapiwienia * graficzna prezentacja lubianych piw * graficzna prezentacja nielubianych piw * graficzny przycisk "podziękuj piwem" - administracja * zarządzanie użytkownikami * zarządzanie grupami i uprawnieniami * zarządzanie biblioteką piw - biblioteka piw * przeglądanie zdjęć i opisów wpisów * wyszukiwarka * poprawianie i sugerowanie tłumaczeń, dodawanie nowych obrazków do galerii piw ==== Określenie scenariuszy użycia ==== - **Scenariusze dla Użytkownika\Gościa**: - Dostęp do serwisu: * Rejestracja w systemie - Wyszukiwanie użytkowników: * Wyszukanie użytkownika * Przeglądnie profilu użytkownika - Przeglądanie treści serwisu: * Wyszukanie informacji na temat piwa - Główne funkcjonalności: * 'Postawienie' piwa za pozytywny wpis na forum - **Scenariusze dla Użytkownika**: - Dostęp do serwisu: * Zalogowanie do systemu * Przypomnienie hasła użytkownika - Zarządzanie kontem: * Zmiana danych użytkownika * Zmiana grafiki do podpisów * Pobranie kodu do podpisu * Dodawanie/edycja lubianych/nielubianych piw - **Scenariusze dla Administratora**: - Zarządzanie użytkownikami: * Zmiana danych (statusu) użytkownika * Usuwanie użytkownika - Zarządzanie grupami i uprawnieniami: * Zmiana uprawnień grupy * Zmiana uprawnień użytkownika - Zarządzanie treścią serwisu: * Moderacja biblioteki piw === Rozwinięcie przykładowych scenariuszy użycia === == Podziękuj piwem == | Nazwa | Podziękuj piwem | | Twórca | Michał Paszko | | Poziom ważności | Ważny | | Typ przypadku użycia | Ogólny | | Aktorzy | Autor wpisu, Adresat wpisu | | Krótki opis | Podziekowanie wirtualnym piwem za pozytywny wpis na serwisie internetowym. | | Warunki wstępne | Autor wpisu musi posiadac aktywne konto w serwisie thankswithbeer.com oraz dodac link prowadzacy do jego profilu - a tym samym umozliwiajacy podziekowanie - na serwisie zawierajacym wpis. | | Warunki końcowe | Ponizej wpisu autora umieszcza sie uprzednio wybrane wirtualne piwo oraz opcjonalny komentarz osoby dziekujacej. Dodatkowo uaktualniaja sie prywatne statystyki autora wpisu (oraz adresata wpisu, jesli posiada aktywne konto) w serwisie thankswithbeer.com. | | Główny przepływ zdarzeń | 1. Czytelnik (adresat) wpisu postanawia podziekowac autorowi wirtualnym piwem. 2. Po kliknieciu na link z podziekowaniem adresat jest przekierowywany na strone profilu autora wpisu w serwisie thankswithbeer.com. 3. Adresat wpisu wybiera rodzaj oraz ilosc wirtualnego piwa, którym chce obdarowac autora wpisu. 4. Statystyki autora wpisu zostaja zaktualizowane w serwisie thankswithbeer.com. | | Alternatywne przepływy zdarzeń | 2a. Adresat wpisu zakłada konto w serwisie thankswithbeer.com 2b. Adresat wpisu - jesli posiada aktywne konto w serwisie thankswithbeer.com - loguje sie do serwisu. 3a. Adresat wpisu dodaje komentarz podziekowania. 4a. Statystyki adresata wpisu - jesli posiada aktywne konto w serwisie thankswithbeer.com zostaja zaktualizowane. | {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:use_case_model.png}} == Moderacja wpisów == | Nazwa | Moderacja wpisów | | Twórca | Leszek Piątek | | Poziom ważności | Mało ważny | | Typ przypadku użycia | Ogólny | | Aktorzy | Autor moderowanego wpisu, Moderator | | Krótki opis | Autor moderowanego wpisu, dodaje nowy wpis (piwo, browar, grupę browarniczą), który później jest akceptowany lub nie przez moderatora. | | Warunki wstępne | Moderator posiada odpowiedni typ konta w naszym serwisie. Autor moderowanego wpisu wchodzi na odpowiednią stronę dodawania wpisów. | | Warunki końcowe | Wpis dokonany przez Autora moderowanego wpisu jest akceptowany lub też nie przez administratora. | | Główny przepływ zdarzeń | 1. Autor moderowanego wpisu Dodaje wpis. 2. Wpis jest dodawany i zapisywany w bazie. Wpis oznaczany jest jako do moderacji - nie wyświetla się innym użytkownikom. 3. Moderator loguje się do systemu i przechodzi na panel moderacji, gdzie ma listę wpisów. 4. Moderator przechodzi na każdy wpis i dokonuje weryfikacji poprawności wpisów. 5. Moderator akceptuje wpis - wpis staje się widoczny dla wszystkich. 6. Moderator usuwa wpis - usuwamy wpis z bazy. | {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:use_case_model2.jpg}} ==== Identyfikacja funkcji ==== * Logowanie/wylogowywanie z serwisu * Dodanie/usunięcie/modyfikacja konta użytkownika * Dodanie/usunięcie/modyfikacja informacji o piwach oraz browarach * Wybór preferowanych oraz nie preferowanych piw przez użytkownika * Modyfikacja //stanu zapiwienia// użytkownika * Generowanie elementu graficznego przedstawiającego //stan zapiwienia// * Moderacja (uprawnienie dla użytkownika) wpisów w bibliotece piwa * Stworzenie unikalnego linku do profilu piwosza * *Polecanie piw innym użytkownikom * *Sugerowanie piw innych piwoszy na podstawie statystyk użytkowników * *Stawianie //mniej wirtualnego piwa// w postaci wpłat na konto PayPal * Backup bazy danych * - funkcjonalności o mniejszym priorytecie ==== Analiza hierarchii funkcji projektowanej aplikacji ==== === FHD - Functional Hierarchy Diagram === {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:diagram_fhd.png}} ==== Budowa i analiza diagramu przepływu danych ==== === Diagram kontekstowy === {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_kontekstowy.png|}} === Diagram główny === {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_glowny.png|}} === 1. Obsługa użytkownika niezarejestrowanego === {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_1.png|}} == 1.8 Postawienie piwa == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_1_8.png|}} === 2. Obsługa użytkownika zarejestrowanego === {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_2.png|}} == 2.4 Zarządzanie kontem == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_2_4.png|}} == 2.5 Polecanie piw użytkownikom == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_2_5.png|}} == 2.6 Moderacja bazy piw == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_2_6.png|}} === 3. Obsługa administratora === {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_3.png|}} == 3.4 Zarządzanie użytkownikami == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_3_4.png|}} == 3.5 Zarządzanie grupami i uprawnieniami == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_3_5.png|}} == 3.6 Moderacja biblioteki piw == {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_dfd_3_6.png|}} ==== Wybór encji (obiektów) i ich atrybutów ==== **BEER** * name (VARCHAR) * desc* (TEXT) * status* (boolean, czy piwo zaakceptowane w danym języku czy nie) * brewery (FK, BREWERY) * price_eur (Float(10)) - cena za 0,5 * gallery (FK, GALLERY) **LOVEDBEERS** * user (FK, USER) * beer (FK, BEER) * rating (FK, LOVEDBEERRATING) **HATEDBEERS** * user (FK, USER) * beer (FK, BEER) * rating (FK, HATEDBEERRATING) **LOVEDBEERRATING** * name * (VARCHAR) * value (int(2)) **HATEDBEERRATING** * name * (VARCHAR) * value (int(2)) **BREWERY** * name (VARCHAR 150) * desc* (TEXT) * status* (boolean, czy browar zaakceptowane w danym języku czy nie) * brewery_owner (FK, BREWERY_OWNER) **BREWERY_OWNER** * name (VARCHAR 150) * desc* (TEXT) * status* (boolean, czy właściciel zaakceptowany w danym języku czy nie) **BEERING** * who (FK, USER, null=True) * whom (FK, USER) * beer (FK, BEER) * size (ENUM) * referrer (VARCHAR 255, NULL=True) * comment (VARCHAR 160, NULL=True) * timestamp (datetime lub unix_timestamp) * ip (VARCHAR) Uwagi: Tabele USER, GROUP, PERMS, GALLERY i IMAGE wykorzystujemy jako wbudowane modele framework'a Django. MtM = many to many FK = Foreign Key *=pola wielojęzykowe ==== Projektowanie powiązań (relacji) pomiędzy encjami ==== {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:thankswithbeer_erd.png}} ==== Kod SQL (PostgreSQL) bazy danych ==== BEGIN; CREATE TABLE "beers_breweryownercontent" ( "id" serial NOT NULL PRIMARY KEY, "model_id" integer NOT NULL, "language_id" integer NOT NULL REFERENCES "multilanguage_language" ("id") DEFERRABLE INITIALLY DEFERRED, "desc" text NOT NULL, "status" boolean NOT NULL ) ; CREATE TABLE "beers_breweryowner" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(150) NOT NULL, "brewery_owner_id" integer ) ; ALTER TABLE "beers_breweryownercontent" ADD CONSTRAINT "model_id_refs_id_598fc7fb" FOREIGN KEY ("model_id") REFERENCES "beers_breweryowner" ("id") DEFERRABLE INITIALLY DEFERRED; ALTER TABLE "beers_breweryowner" ADD CONSTRAINT "brewery_owner_id_refs_id_3d4e3b7b" FOREIGN KEY ("brewery_owner_id") REFERENCES "beers_breweryowner" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE TABLE "beers_brewerycontent" ( "id" serial NOT NULL PRIMARY KEY, "model_id" integer NOT NULL, "language_id" integer NOT NULL REFERENCES "multilanguage_language" ("id") DEFERRABLE INITIALLY DEFERRED, "desc" text NOT NULL, "status" boolean NOT NULL ) ; CREATE TABLE "beers_brewery" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(150) NOT NULL, "brewery_owner_id" integer REFERENCES "beers_breweryowner" ("id") DEFERRABLE INITIALLY DEFERRED ) ; ALTER TABLE "beers_brewerycontent" ADD CONSTRAINT "model_id_refs_id_2e86a681" FOREIGN KEY ("model_id") REFERENCES "beers_brewery" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE TABLE "beers_beercontent" ( "id" serial NOT NULL PRIMARY KEY, "model_id" integer NOT NULL, "language_id" integer NOT NULL REFERENCES "multilanguage_language" ("id") DEFERRABLE INITIALLY DEFERRED, "desc" text NOT NULL, "status" boolean NOT NULL ) ; CREATE TABLE "beers_beer" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(150) NOT NULL, "brewery_id" integer NOT NULL REFERENCES "beers_brewery" ("id") DEFERRABLE INITIALLY DEFERRED, "price_eur" double precision NOT NULL, "lovers" integer NOT NULL, "haters" integer NOT NULL, "logo" varchar(100) NOT NULL, "bottle" varchar(100), "glass" varchar(100) ) ; ALTER TABLE "beers_beercontent" ADD CONSTRAINT "model_id_refs_id_38ce73d1" FOREIGN KEY ("model_id") REFERENCES "beers_beer" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE TABLE "beers_hatedbeerratingcontent" ( "id" serial NOT NULL PRIMARY KEY, "model_id" integer NOT NULL, "language_id" integer NOT NULL REFERENCES "multilanguage_language" ("id") DEFERRABLE INITIALLY DEFERRED, "name" varchar(100) NOT NULL ) ; CREATE TABLE "beers_hatedbeerrating" ( "id" serial NOT NULL PRIMARY KEY, "value" integer NOT NULL ) ; ALTER TABLE "beers_hatedbeerratingcontent" ADD CONSTRAINT "model_id_refs_id_110e7a67" FOREIGN KEY ("model_id") REFERENCES "beers_hatedbeerrating" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE TABLE "beers_hatedbeer" ( "id" serial NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "beer_id" integer NOT NULL REFERENCES "beers_beer" ("id") DEFERRABLE INITIALLY DEFERRED, "rating_id" integer NOT NULL REFERENCES "beers_hatedbeerrating" ("id") DEFERRABLE INITIALLY DEFERRED ) ; CREATE TABLE "beers_lovedbeerratingcontent" ( "id" serial NOT NULL PRIMARY KEY, "model_id" integer NOT NULL, "language_id" integer NOT NULL REFERENCES "multilanguage_language" ("id") DEFERRABLE INITIALLY DEFERRED, "name" varchar(100) NOT NULL ) ; CREATE TABLE "beers_lovedbeerrating" ( "id" serial NOT NULL PRIMARY KEY, "value" integer NOT NULL ) ; ALTER TABLE "beers_lovedbeerratingcontent" ADD CONSTRAINT "model_id_refs_id_4fbf0e87" FOREIGN KEY ("model_id") REFERENCES "beers_lovedbeerrating" ("id") DEFERRABLE INITIALLY DEFERRED; CREATE TABLE "beers_lovedbeer" ( "id" serial NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "beer_id" integer NOT NULL REFERENCES "beers_beer" ("id") DEFERRABLE INITIALLY DEFERRED, "rating_id" integer NOT NULL REFERENCES "beers_lovedbeerrating" ("id") DEFERRABLE INITIALLY DEFERRED ) ; CREATE TABLE "beers_beersize" ( "id" serial NOT NULL PRIMARY KEY, "size" double precision NOT NULL ) ; CREATE TABLE "beers_beering" ( "id" serial NOT NULL PRIMARY KEY, "who_id" integer REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "whom_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "beer_id" integer NOT NULL REFERENCES "beers_beer" ("id") DEFERRABLE INITIALLY DEFERRED, "beer_size_id" integer NOT NULL REFERENCES "beers_beersize" ("id") DEFERRABLE INITIALLY DEFERRED, "referrer" varchar(255) NOT NULL, "comment" varchar(160) NOT NULL, "timestamp" timestamp with time zone NOT NULL, "ip" varchar(15) NOT NULL ) ; CREATE TABLE "auth_permission" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "content_type_id" integer NOT NULL REFERENCES "django_content_type" ("id") DEFERRABLE INITIALLY DEFERRED, "codename" varchar(100) NOT NULL, UNIQUE ("content_type_id", "codename") ) ; CREATE TABLE "auth_group" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(80) NOT NULL UNIQUE ) ; CREATE TABLE "auth_user" ( "id" serial NOT NULL PRIMARY KEY, "username" varchar(30) NOT NULL UNIQUE, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL, "email" varchar(75) NOT NULL, "password" varchar(128) NOT NULL, "is_staff" boolean NOT NULL, "is_active" boolean NOT NULL, "is_superuser" boolean NOT NULL, "last_login" timestamp with time zone NOT NULL, "date_joined" timestamp with time zone NOT NULL ) ; CREATE TABLE "auth_message" ( "id" serial NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "message" text NOT NULL ) ; CREATE TABLE "auth_group_permissions" ( "id" serial NOT NULL PRIMARY KEY, "group_id" integer NOT NULL REFERENCES "auth_group" ("id") DEFERRABLE INITIALLY DEFERRED, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id") DEFERRABLE INITIALLY DEFERRED, UNIQUE ("group_id", "permission_id") ) ; CREATE TABLE "auth_user_groups" ( "id" serial NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "group_id" integer NOT NULL REFERENCES "auth_group" ("id") DEFERRABLE INITIALLY DEFERRED, UNIQUE ("user_id", "group_id") ) ; CREATE TABLE "auth_user_user_permissions" ( "id" serial NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id") DEFERRABLE INITIALLY DEFERRED, UNIQUE ("user_id", "permission_id") ) ; CREATE TABLE "multilanguage_language" ( "id" serial NOT NULL PRIMARY KEY, "code" varchar(5) NOT NULL, "name" varchar(16) NOT NULL ) ; COMMIT; ==== Wygenerowany dokładny ERD z SQL ==== {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:sql_erd.jpg|}} ==== Projekt diagramów STD ==== {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_std_uzytkownik.png|}}{{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:twb_std_gosc.png|}} ==== Wielojęzyczność ==== W projekcie zastosowaliśmy ulepszone rozwiązanie [[http://code.google.com/p/django-multilingual-model/|multilingualmodel]]. Ulepszenie polegało głównie na lepszej integracji z wbudowanym panelem administracyjnym Django. Całość działania systemu wielojęzykowego obrazuje przykładowy diagram ERD: {{:pl:dydaktyka:ztb:2010:projekty:thankswithbeer:language_erd.jpg|}} Na obrazku widzimy encję BEERS_BEER (unikalny wpis w bazie, który przechowuje główne informacje dla encji - informacje nie związane z językiem). Dla encji tej mamy powiązanie FK (model_id) w encji BEERS_BEERCONTENT, które zawiera pola do tłumaczenia - w przykładzie jedynie pole DESC i STATUS są tymi polami. Do całości brakuje jeszcze przyporządkowania języka dla danego wpisu w tabeli *CONTENT - zupełnie osobna encją MULTILANGUAGE_LANGUAGE. Takie podwójne tworzenie modeli (jeden podstawowy oraz drugi z powiązany z tłumaczeniami pól) stosujemy dla każdej encji wielojęzycznej. Dzięki takiemu rozwiązaniu możemy dodawać kolejne wersje językowe bez konieczności definiowania ich z góry - nic nie stoi na przeszkodzie aby dodać nowy język do tabeli MULTILANGUAGE_LANGUAGE, a następnie dodać wpis w BEERS_BEERCONTENT Aby zachować spójność danych można dodać jeszcze klucz UNIQUE(language, model_id) do każdego modelu *CONTENT, dzięki czemu na 100% nie będziemy mieli 2 wersji tłumaczeń tego samego języka.