2009-10-26 27 views
53

Gdzie należy zatwierdzić pola modelu przejść w django?Sprawdzanie poprawności pól modelu Django

Mógłbym wymienić co najmniej dwie możliwe opcje: w przeciążonej metodzie .save() modelu lub w metodzie .to_python() podtypu model.Field (oczywiście, aby to działało, należy wpisać niestandardowe pola) .

Możliwe przypadki użycia:

  • gdy jest absolutnie niezbędna do zapewnienia, że ​​pusty ciąg nie zostanie zapisana w bazie danych (blank = False kluczowe argument nie działa tutaj, to na formularzu walidacja tylko)
  • kiedy to jest konieczne w celu zapewnienia, że ​​„wybory” słowo kluczowe argumentem dostaje przestrzegane na db poziomie, a nie tylko w panelu administratora (rodzaj emulacji typ danych enum)

Istnieje również Atrybut poziomu klasy empty_strings_allowed w modelach. Definicja klasy bazowej i klasy pochodne z powodzeniem zastępują ją, jednak nie wydaje się, aby powodowało to jakiś wpływ na poziomie bazy danych, co oznacza, że ​​nadal mogę konstruować model z pustymi polami i zapisywać je w bazie danych. Którego chcę uniknąć (tak, jest to konieczne).

Możliwe implementacje są

na szczeblu terenowym:

class CustomField(models.CharField): 
    __metaclass__ = models.SubfieldBase 
    def to_python(self, value): 
     if not value: 
      raise IntegrityError(_('Empty string not allowed')) 
     return models.CharField.to_python(self, value) 

na poziomie modelu

class MyModel(models.Model) 
    FIELD1_CHOICES = ['foo', 'bar', 'baz'] 
    field1 = models.CharField(max_length=255, 
       choices=[(item,item) for item in FIELD1_CHOICES]) 

    def save(self, force_insert=False, force_update=False): 
     if self.field1 not in MyModel.FIELD1_CHOICES: 
      raise IntegrityError(_('Invalid value of field1')) 
     # this can, of course, be made more generic 
     models.Model.save(self, force_insert, force_update) 

Może ja czegoś brakuje, a można to zrobić łatwiej (i czystsze)?

Odpowiedz

55

Django ma system model validation na miejscu od wersji 1.2.

W komentarzach sebpiq mówi: "Ok, teraz jest miejsce, aby umieścić model sprawdzania poprawności ... z tym wyjątkiem, że jest uruchamiany tylko przy użyciu ModelForm! Więc pozostaje pytanie, gdy konieczne jest zapewnienie, że walidacja jest przestrzegana na poziom db, co powinieneś zrobić ?, gdzie można wywołać full_clean? "

Nie jest możliwe za pomocą sprawdzania poprawności na poziomie Pythona, aby zapewnić, że sprawdzanie poprawności jest przestrzegane na poziomie db. Najbliższe to prawdopodobnie wywołanie full_clean w nadpisanej metodzie save. Nie dzieje się to domyślnie, ponieważ oznacza to, że wszyscy, którzy nazywają tę metodę zapisu, będą teraz lepiej przygotowani do złapania i obsługi ValidationError.

Ale nawet jeśli to zrobisz, ktoś może nadal aktualizować instancję modelu luzem przy użyciu queryset.update(), co obejdzie tę walidację.Nie ma sposobu, aby Django mógł wdrożyć rozsądnie efektywny model queryset.update(), który mógłby nadal przeprowadzać walidację na poziomie Pythona na każdym zaktualizowanym obiekcie.

Jedynym sposobem, aby naprawdę zagwarantować integralność poziomu db, jest ograniczenie na poziomie db; każda walidacja, którą wykonujesz za pomocą ORM, wymaga od twórcy kodu aplikacji informacji o tym, kiedy wymuszana jest walidacja (i obsługa niepowodzeń sprawdzania poprawności).

Z tego powodu sprawdzanie poprawności modelu jest domyślnie egzekwowane tylko w wersji ModelForm - ponieważ w modelu ModelForm istnieje już oczywisty sposób obsługi ValidationError.

+0

Świetnie, świetnie, po prostu świetnie! Przeszukałem źródło (tak przy okazji, czy jest jakikolwiek sposób uzyskania dostępu do dokumentacji, jak można to zrobić z pnia?) I wydaje mi się, że to właśnie jest to, czego potrzebuję. Chodzi mi o to, że jestem sam dla siebie, ale django jest doskonały w zapewnieniu jednolitego sposobu robienia rzeczy (no, w każdym razie, IMO). – shylent

+2

Sprawdź tę gałąź, upewnij się, że masz zainstalowane docutils i Sphinx, a następnie przejdź do katalogu docs/i uruchom "make html". To powinno budować dokumenty w formie HTML, tak jak są one w witrynie Django, i można uzyskać do nich dostęp lokalnie. –

+0

Ok, wróciłem z czytania źródła (w szczególności 'models/fields/__ init __. Py', models/base.py i core/validators.py), ponieważ dokumentacja nie mówi nic o sprawdzaniu poprawności modelu. Należy jednak zauważyć, że działa prawie tak samo jak sprawdzanie formularzy (przynajmniej ogólna logika jest mniej więcej taka sama). W każdym razie, tego właśnie szukałem. Mam tylko nadzieję, że moje aplikacje nie złamią się okropnie, jeśli przerzucę się z bagażnika do tej gałęzi. – shylent

1

Jeśli Rozumiem, że „wyraźnie” - należy zastąpić funkcję get_db_prep_save zamiast to_python

+0

To rzeczywiście dobry pomysł. Jednakże specjalnie wspomniałem .to_python(), ponieważ zostaje wywołane po zainicjowaniu pola (jeśli podasz __metaclass__ = models.SubfieldBase), więc sprawdzanie poprawności odbywa się wcześnie, co oznacza, że ​​nie możesz nawet zainicjować modelu, jeśli przechodzisz złe wartości dla pól. Być może jednak twoja droga jest właściwa. Przynajmniej to ma dla mnie sens. – shylent

+0

, więc zastąp oba – Oduvan

3

Problem root to, że walidacja powinna nastąpić na modelach. Zostało to omówione od dłuższego czasu w django (sprawdzanie poprawności modelu formularza wyszukiwania na liście mailingowej dev). Prowadzi to do duplikacji lub rzeczy wymykających się walidacji przed trafieniem do bazy danych.

Chociaż nie trafia tułowia, Malcolma "poor man's model validation solution" jest prawdopodobnie najczystszym rozwiązaniem, aby uniknąć powtórzenia się.

+1

Ten link jest uszkodzony dla mnie ... –

+0

Przeczytałem jego zawartość w pamięci podręcznej Google. Ma to sens, tak. Nie jest zbyt pomocne, jeśli nie zamierzam używać formularzy (nie potrzebuję formularzy do wprowadzania danych, prawda?), Ale jest to na pewno sposób na uniknięcie powtórzenia się. – shylent

6

Myślę, że chcesz to ->

from django.db.models.signals import pre_save 

def validate_model(sender, **kwargs): 
    if 'raw' in kwargs and not kwargs['raw']: 
     kwargs['instance'].full_clean() 

pre_save.connect(validate_model, dispatch_uid='validate_models') 

(Skopiowane z http://djangosnippets.org/snippets/2319/)

+0

Nie, niezupełnie. sprawdzanie poprawności modelu jest obecnie w django stabilne, więc punkt pierwotnego pytania jest trochę dyskusyjny – shylent

+2

Nie sądzę, ponieważ walidatory nie uruchamiają się po zapisaniu, więc pozwalają dodawać rzeczy, które nie uprawomocnić. Z http://docs.djangoproject.com/en/dev/releases/1.2/#model-validation "Po prostu wywołanie metody save() instancji modelu nie spowoduje żadnej weryfikacji danych instancji." Chociaż może nie rozumiem, co OP chce, jednak * ja * z pewnością chciałbym mieć sposób, aby zapisać() potwierdzić rzeczy :) – Darius

Powiązane problemy