2013-05-10 10 views
11

Mam kilka pól w stronę wyłączone jak na przykład: (przy wykorzystaniu systemu jinja2 szablonów)pole niepełnosprawnych jest uważany za walidacji WTForms i kolby

<html> 
<body> 
<form action="" method=POST> 
    {{ form.name(disabled=True) }} 
    {{ form.title }} 
    -- submit button -- 
</form> 
</body> 
</html> 

pole jest wyłączone w formie, jak oczekiwano.

W moim views.py: Wykonanie funkcji validate_on_submit() w formularzu przesyłania kończy się niepowodzeniem z błędem sprawdzania poprawności w polu "nazwa", które jest wyłączone. Miałem nadzieję, że sprawdzanie poprawności ignoruje wyłączone pole. Czy to właściwe zachowanie? Jeśli tak, czy możesz powiedzieć, jak sobie z tym poradzić?

Aktualizacja:

class TeamForm(wtf.Form): 
    name = wtf.TextField("Team Name", validators=[validators.Required()]) 
    title = wtf.TextField("Title", validators=[validators.Required()]) 
+0

Czy możesz udostępnić swój kod formularza? – codegeek

+0

@codegeek: Zaktualizowano kod formularzy. – rajpy

+0

Czy możesz podać trochę informacji na temat tego, jaki jest Twój cel? "name" jest polem wymaganym, ale nie pozwalasz na podanie wartości. Czy planujesz wstrzyknąć jeden programowo? – dirn

Odpowiedz

19

To jest rzeczywiście ciekawy problem, a sposób WTForms rozwiązuje to celowo coś, co wymaga jednoznaczność, ponieważ ma to związek z bezpieczeństwem i nie pozwalając użytkownikom na fałszywych danych wejściowych.

Chodzi o to, że "menedżerowie" nie mogą edytować nazwy, podczas gdy "administratorzy" mogą.

Na pierwszy rzut oka wydaje się to oczywiste, wystarczy wyłączyć pola w HTML i napisz swój pogląd takiego:

def edit_team(): 
    form = TeamForm(request.POST, obj=team) 
    if request.POST and form.validate(): 
     form.populate_obj(team) # <-- This is the dangerous part here 
     return redirect('/teams') 
    return render('edit_team.html') 

Jak napisano, jest to poważne zagrożenie dla bezpieczeństwa, ponieważ własność niepełnosprawnych w formularzach HTML jest po stronie klienta tylko. Każdy z inspektorem HTML (tj FireBug, WebKit Inspektor dokumentów, itp) można usunąć tę właściwość, czy ktoś może po prostu złożyć zamówienie tak:

POST /edit_team/7 HTTP/1.0 
Content-Type: application/x-urlencoded 

team=EVILTEAMNAME&title=foo 

Kwestia następnie jest oczywiście jak mamy brama to właściwie po stronie serwera, odpowiadające odpowiedniemu sposobowi wykonania tego? Prawidłowe podejście z WTForms to nie ma pola na pierwszym miejscu. Można to zrobić na kilka sposobów, jednym jest użycie kompozycji formularzy i np. ManagerTeamForm i AdminTeamForm (czasami jest to lepsze), ale innym razem łatwiej jest use del to remove specific fields.

Więc oto jak można napisać swój pogląd, a nie mieć problemów Walidacja:

def edit_team(): 
    form = TeamForm(request.POST, obj=team) 
    if user.role == 'manager': 
     del form.name 
    if request.POST and form.validate(): 
     form.populate_obj(team) 
     return redirect('/teams') 
    return render('edit_team.html') 

i szybki modyfikację szablonu:

<html> 
<body> 
<form action="" method=POST> 
    {% if 'name' in form %} 
     {{ form.name() }} 
    {% else %} 
     {{ team.name|e }} 
    {% endif %} 
    {{ form.title }} 
    -- submit button -- 
</form> 
</body> 
</html> 

Niektóre kawałki odniesienia dla wtforms najlepsze- praktyki:

+0

Dzięki za szczegółowe wyjaśnienie. Skończyło się na usuwaniu pól formularza, jak powiedziałeś. Ale idealnie "wyłączone" pole nie zostanie wysłane do przetwarzania formularzy. Czy to nie prawda? – rajpy

+0

WTForms nie interpretuje atrybutów wyświetlanych w szablonie, takich jak wyłączone, wyświetlanie, widoczność, tylko do odczytu itd. (Jest ich tak wiele i są one bardzo zróżnicowane), należy również wziąć pod uwagę, że renderowanie odbywa się po sprawdzeniu poprawności (i generalnie renderowanie nie występuje, jeśli Formularz sprawdza poprawność), więc jedynym sposobem na przekazanie wtformy, na które nie należy patrzeć, jest usunięcie tego pola. – Crast

+1

@rajpy, być może "wyłączony" jest mylące. Atrybut ten określa, czy użytkownik może zmieniać wartość, a nie czy ma wpływ na zachowanie na serwerze. Czasami pomocne jest pokazanie użytkownikowi, jakie dane są wysyłane na serwer, ale nie pozwalają im na zmianę wartości. – MageWind

1

Trzeba uczynić pole Nazwa opcjonalnie przy określaniu formy.

name = wtf.TextField("Team Name", validators=[validators.Optional()]) 

Następnie w widokach, przekazać zmienną o nazwie „rola” i ustawić go albo zarządca lub administrator w zależności od użytkownika.

<form action="" method=POST> 
{% if role == 'manager' % } 
    {{ form.name(disabled=True) }} 
{% else % } 
    {{ form.name() }} 
{{ form.title }} 
-- submit button -- 
</form> 
+0

W tym przypadku będzie działać. Ale chcę, aby nazwa była dostarczana przez administratora podczas tworzenia zespołu i nie powinna być opcjonalna. – rajpy

+2

Jest to również zagrożenie dla bezpieczeństwa, ponieważ "menedżer" poczty może nadal przesyłać dane, nawet jeśli pole jest wyłączone po stronie klienta, a każdy może usunąć dane. – Crast

+0

Działa, ale dokonuje sprawdzenia bezpieczeństwa przed zapisaniem wartości. – Thiago

0

zdefiniowałem mój własny walidator dla tego problemu:

from wtforms.validators import Optional 

class OptionalIfDisabled(Optional): 

    def __call__(self, form, field): 
     if field.render_kw is not None and field.render_kw.get('disabled', False): 
      field.flags.disabled = True 
      super(OptionalIfDisabled, self).__call__(form, field) 

I wtedy określono nową bazę dla moich formularzy:

from wtforms.form import Form 

class BaseForm(Form): 

    def populate_obj(self, obj): 
     for name, field in self._fields.items(): 
      if not field.flags.disabled: 
       field.populate_obj(obj, name) 

Teraz każda forma może przedłużyć BaseForm i wyłączanie pola takie jak ten:

from wtforms.fields import StringField, SubmitField 

class TeamForm(BaseForm): 
    team = StringField(label='Team Name', 
         validators=[OptionalIfDisabled(), InputRequired()] 
    submit = SubmitField(label='Submit') 

    def __init__(self, *args, **kwargs): 
     super(TeamForm, self).__init__(*args, **kwargs) 
     # disable the fields if you want to 
     if some_condition: 
      self.team.render_kw = {'disabled': True} 

Po walidacji TeamForm, można użyć populate_obj skopiować odblokowane danych formularza w dowolnym obiekcie. Zignoruje wyłączone pola.

Powiązane problemy