2011-01-11 12 views
5

Mam formularz, za pomocą którego chcę zaktualizować obiekt MyModel. W modelu występuje ograniczenie unique_together, pole A wraz z fieldB. W formularzu w czystej metodzie sprawdzam to unikalne ograniczenie.wyłączone pole nie zostało przekazane - wymagane obejście

Z pewnych powodów muszę pokazywać pole A jako tylko do odczytu w aktualizacji. Zatem pole A nie jest przepuszczane. Mój problem polega na tym, że jeśli formularz nie zostanie zatwierdzony, formularz zostanie ponownie wyświetlony, ale straciłem wartość w polu A.

Próbowałem zresetować clean_data ['fieldA'], ale to nie działa. Każdy pomysł, co zmienić?

Forms.py

class MyModelUpdateForm(forms.ModelForm): 
    class Meta: 
     model = MyModel 

    def __init__(self, *args, **kwargs): 
     super(MyModelUpdateForm, self).__init__(*args, **kwargs) 
     self.fields['fieldA'].widget.attrs['readonly'] = True 
     self.fields['fieldA'].widget.attrs['disabled'] = True 

    def clean(self): 
     cleaned_data = self.cleaned_data 
     fieldA= self.instance.fieldA 
     fieldB = cleaned_data.get("fieldB") 

     if MyModel.objects.filter(fieldA=fieldA, fieldB=fieldB).count() > 0: 
      #try to reset fieldA, since it is not passed through, since it is disabled 
      cleaned_data['fieldA'] = fieldA.pk #does not work 
      raise forms.ValidationError('some unique validation error') 
     return cleaned_data 

Views.py:

myModelobject = get_object_or_404(MyModel.objects, pk=mymodel_id) 

    if request.method == 'POST': 
     model_form = MyModelUpdateForm(request.POST, instance=myModelobject) 

     if model_form .is_valid(): 
      .... 

Odpowiedz

11

miałem trochę zabawy patrząc w jaki sposób tworzy dzieła i podszedł z wielu rozwiązań, tylko dla heck tego.

Ponieważ wyłączasz widżet, a nie pole, jeśli chodzi o formularz, zawsze nic nie otrzymuje dla pola A, co zawsze kończy się niepowodzeniem.

Próbowanie czegoś w metodzie clean() nie pomoże w przypadku nieprawidłowych formularzy, ponieważ dane są przetwarzane w postaci clean().

Wygląda na to, że sposób pobierania danych formularzy dla wyświetlacza HTML to field.data, czyli połączenie z field.widget.value_from_datadict(POST, FILES, field_name), dzięki czemu zawsze będzie się wyświetlać dane POST.

Więc myślę, że masz kilka opcji. Hack request.POST, włam się do wewnętrznych danych POST formularza lub zhakuj value_from_datadict.


Hakowanie request.POST: prosto do przodu, ma sens.

myModelobject = get_object_or_404(MyModel.objects, pk=mymodel_id) 

     if request.method == 'POST': 
      POST = request.POST.copy() 
      POST['fieldA'] = myModelobject.fieldA 
      model_form = MyModelUpdateForm(POST, instance=myModelobject) 

      if model_form .is_valid(): 
       # ... 

Hacking wewnętrznego słownika:

def __init__(self, *args, **kwargs): 
    super(MyModelUpdateForm, self).__init__(*args, **kwargs) 
    self.data.update({ 'fieldA': self.instance.fieldA }) 

Hacking value_from_datadict: trochę śmiesznie, ale pokazuje, co można dowiedzieć się z kopania do źródła

def __init__(self, *args, **kwargs): 
    super(MyModelUpdateForm, self).__init__(*args, **kwargs) 
    self.fields['fieldA'].widget.value_from_datadict = lambda *args: self.instance.first_name 

nauczyłem się kilku fajnych rzeczy tutaj:) Nadzieja to pomaga.

+0

hackign request.POST było rozwiązanie! dzięki za to! –

+1

Dobrze, ale co robisz w przypadku pól wyboru. Czy rozróżniasz pole wyboru wyłączone lub niezaznaczone. –

+0

Zauważyłem, że używanie "tylko do odczytu" dla elementu wejściowego jest lepsze niż hakowanie POST lub ... http://www.w3schools.com/tags/att_input_readonly.asp –

0

Mam do czynienia z podobnym problemem i tak go rozwiązałem.

ustawić pole jako ukryty:

self.fields['fieldA'].widget.attrs['style'] = 'display:none;' 

W szablonie pokażę wartość pola oddzielnie:

{{ form.fieldA.label_tag }} 
{{ form.fieldA }} 
{{ form.fieldA.value }} 
{{ form.fieldA.errors }} 

W przypadku fieldA jest wybierz menu:

{{ form.fieldA.label_tag }} 
{{ form.fieldA }} 
{% for value, title in form.fields.fieldA.choices %} 
    {% if value == form.fieldA.value %} 
     {{ title }} 
    {% endif %} 
{% endfor %} 
{{ form.fieldA.errors }} 
1

Można umieść go w klasie formularza:

class MyForm(forms.Form): 

    MY_VALUE = 'SOMETHING' 
    myfield = forms.CharField(
     initial=MY_VALUE, 
     widget=forms.TextInput(attrs={'disabled': 'disabled'}) 

    def __init__(self, *args, **kwargs): 

     # If the form has been submitted, populate the disabled field 
     if 'data' in kwargs: 
      data = kwargs['data'].copy() 
      self.prefix = kwargs.get('prefix') 
      data[self.add_prefix('myfield')] = MY_VALUE 
      kwargs['data'] = data 

     super(MyForm, self).__init__(*args, **kwargs) 

Sposób działania, czy sprawdza, czy jakiekolwiek dane zostały przekazane do konstruktora formularzy. Jeśli tak, kopiuje go (niezabezpieczone dane są niezmienne), a następnie umieszcza wartość początkową przed kontynuowaniem tworzenia formularza.

+0

Podczas gdy ten link może odpowiedzieć na pytanie, lepiej umieścić tutaj istotne części odpowiedzi i podać odnośnik. Odpowiedzi dotyczące linków mogą stać się nieprawidłowe, jeśli strona z linkami się zmieni. –

+0

Ok, edytowałem to tak, że ma całą odpowiedź w. – seddonym

0

Rozwiązałem taki problem bez dotykania części backendu, wystarczy dodać konkretną klasę do pola A w pliku forms.py z właściwością "widoczność: ukryta".

self.fields['fieldA'].widget.attrs['class'] = 'visibility_hidden_class' 

następnie w szablonie Twój form.fieldA zostaną ukryte, ale nie stracił na żądanie POST

{{ form.fieldA.label_tag }} 
{{ form.fieldA }} 
<input type="text" value="{{ form.fieldA.value }}" disabled /> 

więc będzie jeszcze swoją form.fieldA w danych formularza zapytanie. I wizualnie twoje pole będzie zawsze wypełnione form.fieldA.value.

0

Musiałem rozwiązać podobny problem, użyłem formuły dinamyc do wygenerowania nowego wiersza, więc gdy wybierzesz produkt, który został pokazany jako wyłączony, ale trzeba uzyskać wartość na formularzu w widoku, chyba Jquery i JavaScript był dla mnie łatwiejszy, więc generowałem zdarzenie przed wysyłaniem, wyłączając wyłączone na selektorze, którego używam w moich zestawach formularzy, używałem widgetu select2 dla tej konfiguracji, więc działa również z select2:

Oczywiście przycisk wysłać id = "postForm" i identyfikator forma id = "contractForm"

$('#postForm').click(function(e){ 
      e.preventDefault(); 
      $('select[id^="id_p_v"][id$="product"]').each(function(){ 
       // getIdNumber returns the digit value from id_field-n- in formset value 
       // p_v was the prefix of fomset 
       var id = getIdNumber($(this).attr('id')); 
       // I have an empty form for new forms so skip that one actually on send doesn't matter really. (form POST) but anyway skip 
       if(id != null){ 
        $(this).prop("disabled", false); 
       } 
      }); 
      $('#contractForm').submit(); 
      }); 
1

użyłem j Zapytanie, aby rozwiązać problem przez usunięcie wyłączone ze wszystkich danych wejściowych przed przesłaniem.

$('#my_form').submit(function(){ 
    $("#my_form :disabled").removeAttr('disabled'); 
}); 

Used answer from another SO answer

Powiązane problemy