2014-04-30 26 views
5

Chciałbym wdrożyć różne systemy pomiarowe w moim projekcie Django, aby użytkownicy mogli wybrać, czy chcą używać jednostek metrycznych lub imperialnych. Jednak nie wiem, jakie jest prawidłowe podejście do tego.Koncepcja różnych systemów pomiarowych w projekcie Django

Obecnie moje modele nie mają pól znających jednostkę miary (są polami całkowitymi/dziesiętnymi) i wolałbym, aby nie zmieniać bezpośrednio pól.

Ponieważ moje obecne wartości w bazie danych już reprezentują wartości metryczne, planuję zachować je w ten sposób, co oznacza, że ​​będę musiał obsłużyć konwersję danych wejściowych/wartości użytkownika. Pint wydaje się być świetną biblioteką do tego celu.

Czy jest to możliwe bez edytowania pól w aplikacji, ale przy użyciu registration patterns lub czegoś innego? To, co chciałbym osiągnąć, to coś takiego:

  1. Definiuję strukturę danych o różnych typach pomiarów i możliwych wartościach, takich jak długość: metry, stopy; waga: kilogramy, funty itp.
  2. Dodaję nowy plik "measurements.py" lub coś podobnego do każdego katalogu aplikacji, który ma pola, które przechowują wartości pomiarowe. W tym polu mogę określić, które dokładnie są polami pomiaru i jakie są ich typy, takie jak pola = {mymodelfield: length, myothermodelfield: weight} itd.
  3. Niektóre ustawienia domyślne można ustawić w pliku ustawień i nadpisać w aplikacji plik, taki jak domyślna jednostka dla każdego pomiaru (jednostka przechowywana w bazie danych).
  4. Użytkownik ustawia swoje preferencje dla każdego typu pomiaru. Istnieje domyślna jednostka dla każdego typu pomiaru, jak wspomniano w poprzednim punkcie. Konieczna jest logika do konwersji danych wejściowych użytkownika w przypadku niezgodności ustawień domyślnych/domyślnych (przechowywanych). Zawieszone formularze powinny również być w stanie przekonwertować wartość pola z powrotem z domyślnej na preferowaną przez użytkownika.

Tego rodzaju rozwiązanie ułatwiłoby podłączenie go do istniejących aplikacji, ponieważ żadne pola i formularze nie zostałyby zmienione bezpośrednio w aplikacji. Podstawowe przykłady wykorzystujące wzorce rejestracji byłyby pomocne.

Wszelkie istotne informacje są mile widziane, w tym ogólne pomysły i wzorce, jak to robi się w projektach innych niż Django.

+0

udało mi się osiągnąć konwersji jednostek z pól niestandardowych opartych na odpowiedź Dana i to [Odwołanie się] (http://tothinkornottothink.com/post/10815277049/django-forms-i- niestandardowe pola i widżety w szczegółach). –

Odpowiedz

3

Ponieważ chcesz nadal przechowywać w metrykę następnie tylko raz masz zamiar ewentualnie przekształcić to:

  1. Wprowadzane przez użytkownika
  2. Wyświetlanie danych użytkownikowi

Dla danych wejściowych użytkownika możesz użyć niestandardowego MultiValueField i niestandardowego MultiWidget, który wyświetli wartość metryki.

from django import forms 
from django.forms import widgets,Form, CharField, MultiValueField, ChoiceField, DecimalField 

imperial = "imp" 
imperial_display = "Miles" 
metric = "met" 
metric_display = "Kilometers" 
unit_types = ((imperial, imperial_display), (metric, metric_display)) 

#You would use that library instead of this, and maybe not floats 
def to_imperial(val): 
    return float(val) * 1.60934 

def to_metric(val): 
    return float(val)/0.62137 

class NumberUnitWidget(widgets.MultiWidget): 
    def __init__(self, default_unit=None, attrs=None): 
     #I feel like this default_unit thing I'm doing is ugly 
     #but I couldn't think of another way to get the decompress 
     #to know which unit to convert to 
     self.default_unit = default_unit 

     _widgets = [ 
      widgets.NumberInput(attrs=attrs), 
      widgets.Select(attrs=attrs, choices=unit_types), 
     ] 
     super(NumberUnitWidget, self).__init__(_widgets, attrs) 

    #convert a single value to list for the form 
    def decompress(self, value): 
     if value: 
      if self.default_unit == imperial: 
       return [to_imperial(value), self.default_unit] 

      #value is the correct format 
      return [value, self.default_unit] 
     return [0, self.default_unit] 

class NumberUnitField(MultiValueField): 

    def __init__(self, *args, **kwargs): 
     _widget = NumberUnitWidget(default_unit=kwargs['initial'][0]) 
     fields = [ 
      DecimalField(label="Number!"), 
      ChoiceField(choices=unit_types) 
     ] 
     super(NumberUnitField, self).__init__(fields=fields, widget = _widget, *args, **kwargs) 


    def compress(self, values): 
     if values[1] == imperial: 
      #They inputed using imperial, convert to kilo 
      return to_metric(values[0]) 
     return values[0] #You dont need to convert because its in the format you want 

class TestForm(Form): 
    name = CharField() 
    num = NumberUnitField(initial=[None, metric]) 

Ustawienie domyślne metryki dla num w TestForm mogą być nadpisane podczas wystąpienia formularz używając initial={'num':[None,'imp']} tak to gdzie można wstawić preferencji użytkowników ze swojego profilu.

Korzystając z powyższego formularza, gdy zobaczysz, że formularz is_valid() dane, które formularz wróci do Ciebie, będą miały charakter metryczny.

Gdy użytkownik zobaczy formularz, pojawi się jako Django NumberInput, a następnie wybierz z wybranym domyślnym ustawieniem.

Do wyświetlania danych można użyć template filter. Oto prosta, aby dać wyobrażenie

from django import template 
register = template.Library() 
@register.filter 
def convert_units(value, user_pref): 
    #You probably want to use that library you found here to do conversions 
    #instead of what i've used. 
    if user_pref == "met": 
     return str(value) + " Kilometers" 
    else: 
     return str(float(value)/ 1.60934) + " Miles" 

Następnie w szablonie to wyglądać mniej więcej tak:

{{a_value|convert_units:user_pref}} 

Oczywiście wszystko to tylko konwersja mil/kilometrów. Będziesz musiał duplikować i zmieniać lub podklasować widget/pole i prawdopodobnie będziesz mieć więcej filtrów szablonów, aby objąć inne rzeczy, takie jak funty/kilogramy.

+0

Dzięki za szczegółową odpowiedź! Będę nad tym dzisiaj pracował, aby zobaczyć, jak to działa. P.S. Czy byłoby w tym przypadku możliwe wykorzystanie wcześniej wymienionych wzorców rejestracji? Na przykład. coś podobnego do tego, co robi wyszukiwanie Haystack z SearchIndexes? –

+0

Aby powiązać te formularze ze swoimi modelami? Może, ale powinieneś spojrzeć na ModelForm https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#modelform. –