Strona główna   Pomoc Zaloguj się Rejestracja  
Witamy, Gość. Zaloguj się lub zarejestruj.
Czy dotarł do Ciebie email aktywacyjny?

Zaloguj się podając nazwę użytkownika, hasło i długość sesji

Aktualności: Uwaga! Do połowy września będą trwały gruntowne porządki na forum.
Szukaj
Strony: [1] 2   Do dołu
Drukuj
Wątek: [Django] Nazwy rejestrowanych aplikacji w PA  (Przeczytany 524 razy)
« : 11:33 22/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37


Witam.

Dostrajam właśnie panel administracji w Django. W jaki sposób zmienić wyświetlane nazwy zarejestrowanych aplikacji?

Np. żeby zamiast "Auth" było "Zarządzanie użytkownikami", albo zamiast "Sites" - "Instancje", zamiast "News" - "Zarządzanie treścią" itd?
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
« Odpowiedz #1 : 12:05 22/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37


Nie zrozumiałeś mnie. To co napisałeś, dotyczy zarejestrowanych modeli z danej aplikacji, taką klasę Meta mam zdefiniowaną. Chodzi o to, że mam aplikację "news" (python manage.py startapp news), w models.py mam zdefiniowane dwa modele, a w nich klasy Meta z tymi parametrami, i to jest ok, ich nazwy są takie, jak w klasie Meta, ale jak zmienić nazwę wyświetlanej APLIKACJI (czyli "news"), a nie poszczególnych modelów w niej zdefiniowanych.

Patrz obrazek, chcę zmienić nazwy auth, news, sites.
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
« Odpowiedz #2 : 12:50 22/07/10 »
kelso Offline
New Python User

Zobacz profil
*

Reputacja: 0
Wiadomości: 9


Właśnie przeczytałem twój post jeszcze raz, później swój i go usunąłem Mrugnięcie.
Zapisane
« Odpowiedz #3 : 14:10 22/07/10 »
Sharpek Offline
New Python User

Zobacz profil WWW
*

Reputacja: 4
Płeć: Mężczyzna
Wiadomości: 14


Może użyj grappelli:

http://code.google.com/p/django-grappelli/wiki/customizingindex
Zapisane
« Odpowiedz #4 : 04:26 23/07/10 »
cji Offline
New Python User

Zobacz profil
*

Reputacja: 14
Wiadomości: 87


Mam wrażenie, że nawet z grappellim sprowadzi się to do grzebania w templates... Chociaż przynajmniej nie będzie problemu z widokami.

Na zwykłej instalacji Django można to również zrobić - trzeba odrobinę pogmerać w kodzie django.contrib.admin.

Konkretnie w:

Cytuj
django/contrib/admin/sites.py

Mamy metodę index() klasy AdminSite, a w niej taki kod:

Kod
                    # ............
                   else:
                       app_dict[app_label] = {
                           'name': app_label.title(),
                           'app_url': app_label + '/',
                           'has_module_perms': has_module_perms,
                           'models': [model_dict],
                       }
                   # ..............

Jak łatwo wywnioskować z urywka django/contrib/admin/templates/admin/index.html:

Kod
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>

Pokazany wyżej atrybut name odpowiada za wyświetlaną nazwę aplikacji.

Co z tym zrobić? Pierwszy pomysł, jaki przychodzi mi do głowy:

1) Zdefiniować gdzieś (settings.py) dict mapujący app_label na verbose_app_name:

Kod
VERBOSE_APP_NAMES = {
   "auth" : "Zarzadzanie uzytkownikami",
   "sites" : "Instancje (nie wiem, czy to dobra nazwa...)",
}

2) W django/contrib/admin/sites.py zmienić wspominaną metodę (okolice 375 linii):

Kod
                    # ............
                   else:
                       app_dict[app_label] = {
                           'name': getattr(settings, "VERBOSE_APP_NAMES", {} ).get( app_label ) or  app_label.title(), # settings sa importowane na poczatku modulu
                           'app_url': app_label + '/',
                           'has_module_perms': has_module_perms,
                           'models': [model_dict],
                       }
                   # ..............

I już, powinno działać. Sam nie testowałem, ale możesz sprawdzić, nie powinno wybuchnąć Uśmiech. Źródełka wzięte z Django 1.2.

Morał1
Django to "tylko python"; nie trzeba się bać modyfikacji i ingerencji w jego mechanizmy, jeśli tylko zachodzi taka potrzeba.

Moral2
Szukanie gotowych rozwiązań czasem jest typowym overkillem: grappelli jest fajny, ale ciężki, a żeby osiągnąć zamierzony przez Ciebie efekt wystarczy modyfikacja jednej linijki... - co dla mnie przynajmniej - jest mniejszym wysiłkiem, niż instalacja grappelli.

Moral3
Virtualenv! Jeśli każdy projekt ma własną instalacje Django, takie modyfikacje są bezpieczne. Co prawda zawsze warto użyć getattr() z wartością domyślną (i inne takie techniki), ale gdybyś o tym zapomniał - Virtualenv zabezpieczy pozostałe projekty przed niespodziewana wysypka.
Zapisane
« Odpowiedz #5 : 08:02 23/07/10 »
Sławek Tuleja Offline
New Python User

Zobacz profil WWW
*

Reputacja: 4
Wiadomości: 35


Cześć.

1. Dla Twoich modeli wystarczy w mecie ustawić:

Kod
class Meta:
   app_label = 'nazwa aplikacji'

2. Faktycznie z nazwami aplikacji w Django jest problem. Ja jednak proponuje zamiast grzebania w kodzie Django, zmienić plik .po (plik z tłumaczeniami).

Pozdrawiam.

A jaka nazwa dla sites? Może domeny lub nazwa strony.
Zapisane

« Odpowiedz #6 : 11:57 23/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37


To chyba nie jest dobra metoda, bo nazwa się zmienia, ALE, potem nie mogę dostać się do zarządzania modelami wchodzącymi w skład danej aplikacji, dostaję błąd:

Cytuj
DatabaseError at /admin/Zarządzanie treścią/wiadomosc/

no such table: Zarządzanie treścią_wiadomosc
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
« Odpowiedz #7 : 12:36 23/07/10 »
cji Offline
New Python User

Zobacz profil
*

Reputacja: 14
Wiadomości: 87


Tłumaczenie może być również niezłym pomysłem - ma ten plus, że zadziała wszędzie, nie tylko na głównej stronie panelu administracyjnego. Do tego są komendy "makemessages" i "compilemessages" - więcej tu:

http://docs.djangoproject.com/en/dev/topics/i18n/localization/#message-files

Ale właściwie czemu nie grzebać w kodzie Django? Od tego jest! [potem można zrobić patch i mieć nadzieje, że wejdzie do samego Django na stale Mrugnięcie]

Niżej screen ze świeżego projektu z powyższa poprawką.

Cytuj
1. Dla Twoich modeli wystarczy w mecie ustawić:

Kod
class Meta:
   app_label = 'nazwa aplikacji'

Cytuj
To chyba nie jest dobra metoda, bo nazwa się zmienia, ALE, potem nie mogę dostać się do zarządzania modelami wchodzącymi w skład danej aplikacji, dostaję błąd:

@Edit:
To też ma szanse zadziałać. Trzeba jednak dodać do Meta tego modelu db_table z odpowiednia wartością.

Mniej więcej tak:

Kod
class Meta:
   app_label = 'nazwa aplikacji'
   db_table = 'app_model' # za app i model podstawiamy odpowiednie wartosci

I to również działa w całym panelu admina - ale nie działa z kolei strona z lista modeli tej wirtualnej aplikacji.

Jeszcze jedna trudność - dla importowanych modeli trzeba by się nagimnastykować, tj. wyrejestrować model z panelu admina, zmienić mu _meta.app_label i _meta.db_table, zarejestrować z powrotem. To mało eleganckie.

Chociaż przyznam się, że widziałem piękny kod, realizujący coś podobnego:

Kod
from django.contrib.auth.models import User, Group
import new
import inspect
 
class MetaClass(type):
   def __new__(self, classname, classbases, classdict):
       try:
           frame = inspect.currentframe()
           frame = frame.f_back
           if frame.f_locals.has_key(classname):
               old_class = frame.f_locals.get(classname)
               for name,func in classdict.items():
                   if inspect.isfunction(func):
                       setattr(old_class, name, func)
               return old_class
           return type.__new__(self, classname, classbases, classdict)
       finally:
           del frame
 
class MetaObject(object):
   __metaclass__ = MetaClass
 
class User(MetaObject):
#etc...

Śliczne Uśmiech!
Zapisane
« Odpowiedz #8 : 15:15 24/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37


Z waszą pomocą poradziłem sobie w następujący sposób:

Pythona i Django umieściłem w Virtualenv. W django/contrib/admin/ utworzyłem custom_utils.py o treści:

Kod
#-*- coding: utf-8 -*-
from django.conf import settings
 
def get_verbose_name(app_label):
   verbose_name = getattr(settings, "VERBOSE_APP_NAMES", {} ).get( app_label )
   if verbose_name:
       return verbose_name
   else:
       return app_label.capitalize()

Do sites.py i options.py odpowiedni import:

Kod
from custom_utils import get_verbose_name

W sites.py w metodach index i app_index do app_dict doklejam:

Kod
'verbose_name' : get_verbose_name(app_label)

A także w metodzie app_index zamieniam:

Kod
'title': _('%s administration') % capfirst(app_label),

Na:

Kod
'title': _('%s administration') % get_verbose_name(app_label),

To samo dodaję do options.py w metodzie render_change_form do context.update oraz context w metodzie changelist_view .

Została sprawa szablonów.

W index.html zamieniam name na verbose_name w linijce:

Kod
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.verbose_name as verbose_name %}{{ verbose_name }}{% endblocktrans %}</a></caption>

A także bloki breadcrumbs w app_index.html oraz change_form.html, odpowiednio w linijkach:

Kod
{% blocktrans with app.verbose_name as verbose_name %}{{ verbose_name }}{% endblocktrans %}

Kod
<a href="../../"> {{ app_verbose_name }}</a> &rsaquo;

Zmodyfikować trzeba także change_list.html, zamieniając:

Kod
<a href="../">
        {{ app_label|capfirst }}
     </a>

Na:

Kod
<a href="../">
        {{ verbose_name }}
     </a>

W settings.py projektu dodałem:

Kod
VERBOSE_APP_NAMES = {
   "auth" : u"Użytkownicy i uprawnienia",
   "sites" : u"Domeny",
   'flatpages' : u'Strony statyczne',
   'news' : u'Treść',
   'ads' : u'Moduł reklam'
}

Po tych zabiegach powinniśmy mieć w całym PA nazwy, jakie zdefiniowaliśmy sobie w settings.py.
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
« Odpowiedz #9 : 19:06 24/07/10 »
cji Offline
New Python User

Zobacz profil
*

Reputacja: 14
Wiadomości: 87


Popraw to:

Kod
def get_verbose_name(app_label):
   try:
       return getattr(settings, "VERBOSE_APP_NAMES", {} ).get( app_label )
   except:
       return app_label

Bo ta linijka:

Kod
return getattr(settings, "VERBOSE_APP_NAMES", {} ).get( app_label )

Nigdy nie zgłosi wyjątku! (co znaczy, ze dla aplikacji nie wymienionej w VERBOSE_APP_NAMES zwroci None) Zamień ją na:

Kod
return getattr(settings, "VERBOSE_APP_NAMES", {} )[ app_label ]

Po za tym nic trudnego, sam widzisz Uśmiech.
Zapisane
« Odpowiedz #10 : 11:38 29/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37


Mam jeszcze jeden element do polonizacji w panelu administracyjnym Django. Chodzi o uprawnienia nadawane użytkownikom (aplikacja auth), w oknie wyboru mam np:

Cytuj
ads | Reklama | Can add Reklama

Nie umiem się dokopać do właściwego kodu, jak to zmienić? Chcę mieć:

Cytuj
Moduł reklam | Reklama | Może dodać Reklama
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
« Odpowiedz #11 : 23:00 29/07/10 »
cji Offline
New Python User

Zobacz profil
*

Reputacja: 14
Wiadomości: 87


Ale masz wymagania... Chichot:

Jest kilka miejsc, w które powinieneś zajrzeć. Pierwsze to:

Cytuj
django/contrib/auth/models.py

Definicja modelu Permission, metoda __unicode__. Wygląda tak:

Kod
def __unicode__(self):
       return u"%s | %s | %s" % (
           unicode(self.content_type.app_label),
           unicode(self.content_type),
           unicode(self.name))

Jeśli zastosujesz tutaj tą samą sztuczkę z VERBOSE_APP_NAMES co wyżej przy self.content_type.app_label - będziesz miał załatwiona sprawę pierwszej części wyświetlanej nazwy uprawnienia. To jest ta prosta cześć.

Trzecia cześć wyświetlanej nazwy, czyli to gdzie chcesz mieć "Może dodać Reklama" jest - jak widać - brane z kolumny name. Dla już utworzonych uprawnień możesz zmienić tą wartość bezpośrednio w bazie, wykonując SQLowe UPDATE albo w manage.py shell korzystając z djangowego ORMa. Natomiast każdy kolejny model, dla którego zostaną automatycznie utworzone uprawnienia, znów będzie miał "Can add..." w tym miejscu.

Żeby to naprawić, musisz zajrzeć do:

Cytuj
django/contrib/auth/management/__init__.py

W tym miejscu aplikacja auth podpina sobie signal handler do post_syncdb.

Interesujący w tym pliku jest ten kawałek:

Kod
def _get_all_permissions(opts):
   "Returns (codename, name) for all permissions in the given opts."
   perms = []
   for action in ('add', 'change', 'delete'):
       perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
   return perms + list(opts.permissions)

Uwaga - nie testowałem. Nie wiem, co się może wysypać (chociaż nic nie powinno), ale wygląda na to, że taka mniej więcej modyfikacja:

Kod
def _get_all_permissions(opts):
   "Returns (codename, name) for all permissions in the given opts."
   perms = []
   for action, action_pl in ( ('add', 'dodać'), ('change', 'zmienić'), ('delete', 'usunąć') ):
       perms.append((_get_permission_codename(action, opts), u'Może %s %s' % (action_pl, opts.verbose_name_raw)))
   return perms + list(opts.permissions)

Powinna załatwić sprawę. (Pamiętaj o dopisaniu # -*- coding: utf8 -*- w pierwszej linii tego pliku, inaczej nie będziesz mógł użyć polskich znaków!)

Z tym, że nie jest to rozwiązanie najlepsze. Zdecydowanie lepiej byłoby skorzystać tutaj z ugettext - choć wtedy potrzebowałbyś mieć włączone I18N i zrobiony plik tłumaczeń, to byłoby to pewnie dobrym dodatkiem do samego Django.
Zapisane
« Odpowiedz #12 : 09:04 30/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37


To mi potrzebne do magisterki (witryna z CMS dla redakcji pewnej gazety), więc chcę to zrobić jak najlepiej, ale ważniejsze jest to, żebym się jak najwięcej Django nauczył, więc samo grzebanie w kodzie też z pewnością się przyda.

Dziś wypróbuję Twoje rady, i dam znać, jakie efekty to przyniosło Uśmiech.
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
« Odpowiedz #13 : 13:17 30/07/10 »
kelso Offline
New Python User

Zobacz profil
*

Reputacja: 0
Wiadomości: 9


Heh, też piszę aplikacje na magisterkę korzystając z Django, dobrze że mam jeszcze rok Mrugnięcie.
Zapisane
« Odpowiedz #14 : 21:49 30/07/10 »
bagheera Offline
New Python User

Zobacz profil
*

Reputacja: 0
Płeć: Mężczyzna
Wiadomości: 37



Jest kilka miejsc, w które powinieneś zajrzeć. Pierwsze to:

Cytuj
django/contrib/auth/models.py

Definicja modelu Permission, metoda __unicode__. Wygląda tak:

Kod
def __unicode__(self):
       return u"%s | %s | %s" % (
           unicode(self.content_type.app_label),
           unicode(self.content_type),
           unicode(self.name))

Jeśli zastosujesz tutaj tą samą sztuczkę z VERBOSE_APP_NAMES co wyżej przy self.content_type.app_label - będziesz miał załatwioną sprawę pierwszej części wyświetlanej nazwy uprawnienia. To jest ta prosta część.

Zrobiłem jak napisałeś, działa.

Cytuj
django/contrib/auth/management/__init__.py

W tym miejscu aplikacja auth podpina sobie signal handler do post_syncdb.

Interesujący w tym pliku jest ten kawałek:

Kod
def _get_all_permissions(opts):
   "Returns (codename, name) for all permissions in the given opts."
   perms = []
   for action in ('add', 'change', 'delete'):
       perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
   return perms + list(opts.permissions)

Uwaga - nie testowałem. Nie wiem, co się może wysypać (chociaż nic nie powinno), ale wygląda na to, że taka mniej więcej modyfikacja:

Kod
def _get_all_permissions(opts):
   "Returns (codename, name) for all permissions in the given opts."
   perms = []
   for action, action_pl in ( ('add', 'dodać'), ('change', 'zmienić'), ('delete', 'usunąć') ):
       perms.append((_get_permission_codename(action, opts), u'Może %s %s' % (action_pl, opts.verbose_name_raw)))
   return perms + list(opts.permissions)

Też działa, zmiany widać po usunięciu danych z bazy i utworzeniu ich na nowo (testowo korzystam z SQLite3), ale trzeba wszędzie skorzystać z łańcuchów unicode:

Kod
('add',u'dodać'), ('change',u'zmienić'), ('delete',u'usunąć')

Ale to nie jest wszystko. O ile mam np:

Cytuj
Moduł reklam | Reklama | Może dodać Reklama

To jest tam też np.:

Cytuj
Administracja | log entry | Może dodać log entry

Więc zostają jeszcze nazwy modeli. Czy podmiana na sztywno w models.py verbose_name z _('log entry') na 'dziennik' to dobry pomysł?

Rozumiem, że nie jest elegancki, bo trzeba wiele drobnych zmian w wielu plikach narobić, ale żeby chociaż się nie posypało nic, a przyniosło pożądany efekt.

Zainteresował mnie docstring w db/models/options.py w:

Kod
def verbose_name_raw(self):
       """
       There are a few places where the untranslated verbose name is needed
       (so that we get the same value regardless of currently active
       locale).
       "
""
       lang = get_language()
       deactivate_all()
       raw = force_unicode(self.verbose_name)
       activate(lang)
       return raw
   verbose_name_raw = property(verbose_name_raw)

A jakby tak nadpisać tę metodę, żeby zwracała tłumaczony verbose_name?
Zapisane

Linux, Python, Django - ciągła nauka. Chętny na staż lub etatową pracę. Lublin lub zdalnie Uśmiech
Strony: [1] 2   Do góry
Drukuj
Skocz do:  

© 2007 - 2010 Polish Python Coders Group
Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC | Theme by PixelSlot