Odpowiedzi:
Odpowiedź Bera - przechowywanie jej w „Threadlocals” - to bardzo zły pomysł. Nie ma absolutnie żadnego powodu, aby to robić w ten sposób.
Dużo lepszym sposobem jest przesłonić formie za __init__
metodę do podjęcia dodatkowego argumentu słowa kluczowego request
. Spowoduje to zapisanie żądania w formularzu tam, gdzie jest to wymagane i skąd możesz uzyskać do niego dostęp w swojej czystej metodzie.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(MyForm, self).__init__(*args, **kwargs)
def clean(self):
... access the request object via self.request ...
i Twoim zdaniem:
myform = MyForm(request.POST, request=request)
ZAKTUALIZOWANO 25.10.2011 : Teraz używam tego z dynamicznie tworzoną klasą zamiast metody, ponieważ w przeciwnym razie Django 1.3 wyświetla pewne dziwactwa.
class MyModelAdmin(admin.ModelAdmin):
form = MyCustomForm
def get_form(self, request, obj=None, **kwargs):
ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
class ModelFormWithRequest(ModelForm):
def __new__(cls, *args, **kwargs):
kwargs['request'] = request
return ModelForm(*args, **kwargs)
return ModelFormWithRequest
Następnie nadpisz MyCustomForm.__init__
w następujący sposób:
class MyCustomForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(MyCustomForm, self).__init__(*args, **kwargs)
Następnie możesz uzyskać dostęp do obiektu żądania z dowolnej metody ModelForm
with self.request
.
__new__
przesłonięcie , dodajesz żądanie do kwargs, które później zostaną przekazane do metody klasy __init__
. Nazwanie klasy ModelFormWithRequest
wydaje mi się o wiele jaśniejsze w tym znaczeniu niż ModelFormMetaClass
.
Co jest warte, jeśli używasz widoków opartych na klasach , zamiast widoków opartych na funkcjach, nadpisz get_form_kwargs
w widoku do edycji. Przykładowy kod dla niestandardowego CreateView :
from braces.views import LoginRequiredMixin
class MyModelCreateView(LoginRequiredMixin, CreateView):
template_name = 'example/create.html'
model = MyModel
form_class = MyModelForm
success_message = "%(my_object)s added to your site."
def get_form_kwargs(self):
kw = super(MyModelCreateView, self).get_form_kwargs()
kw['request'] = self.request # the trick!
return kw
def form_valid(self):
# do something
Powyższy kod widoku będzie request
dostępny jako jeden z argumentów słów kluczowych __init__
funkcji konstruktora formularza . Dlatego w twoim ModelForm
:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
# important to "pop" added kwarg before call to parent's constructor
self.request = kwargs.pop('request')
super(MyModelForm, self).__init__(*args, **kwargs)
request
obiektu w get_form_kwargs
środku.
self.get_object
? CreateView
Rozszerza SingleObjectMixin
. Ale to, czy to zadziała, czy zgłosi wyjątek, zależy od tego, czy tworzysz nowy obiekt, czy aktualizujesz istniejący; tzn. przetestuj oba przypadki (i oczywiście usuń).
Typowym podejściem jest przechowywanie obiektu żądania w odwołaniu lokalnym wątku przy użyciu oprogramowania pośredniczącego. Następnie możesz uzyskać do niego dostęp z dowolnego miejsca w aplikacji, w tym z metody Form.clean ().
Zmiana sygnatury metody Form.clean () oznacza, że masz własną, zmodyfikowaną wersję Django, która może nie być tym, czego chcesz.
Dziękuję, liczba oprogramowania pośredniego wygląda mniej więcej tak:
import threading
_thread_locals = threading.local()
def get_current_request():
return getattr(_thread_locals, 'request', None)
class ThreadLocals(object):
"""
Middleware that gets various objects from the
request object and saves them in thread local storage.
"""
def process_request(self, request):
_thread_locals.request = request
Zarejestruj to oprogramowanie pośredniczące zgodnie z opisem w dokumentacji Django
**kwargs
, co oznacza, że będziesz musiał przekazać obiekt żądania jako MyForm(request.POST, request=request)
.
Dla administratora Django, w Django 1.8
class MyModelAdmin(admin.ModelAdmin):
...
form = RedirectForm
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
form.request = request
return form
Napotkałem ten konkretny problem podczas dostosowywania administratora. Chciałem, aby pewne pole zostało zweryfikowane na podstawie danych uwierzytelniających konkretnego administratora.
Ponieważ nie chciałem modyfikować widoku, aby przekazać żądanie jako argument do formularza, wykonałem następujące czynności:
class MyCustomForm(forms.ModelForm):
class Meta:
model = MyModel
def clean(self):
# make use of self.request here
class MyModelAdmin(admin.ModelAdmin):
form = MyCustomForm
def get_form(self, request, obj=None, **kwargs):
ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
def form_wrapper(*args, **kwargs):
a = ModelForm(*args, **kwargs)
a.request = request
return a
return form_wrapper
obj=obj
nie obj=None
w linii 11.
'function' object has no attribute 'base_fields'
. Jednak prostsza (bez zamknięcia) odpowiedź @ François działa płynnie.
Nie zawsze możesz użyć tej metody (i jest to prawdopodobnie zła praktyka), ale jeśli używasz formularza tylko w jednym widoku, możesz określić zakres wewnątrz samej metody widoku.
def my_view(request):
class ResetForm(forms.Form):
password = forms.CharField(required=True, widget=forms.PasswordInput())
def clean_password(self):
data = self.cleaned_data['password']
if not request.user.check_password(data):
raise forms.ValidationError("The password entered does not match your account password.")
return data
if request.method == 'POST':
form = ResetForm(request.POST, request.FILES)
if form.is_valid():
return HttpResponseRedirect("/")
else:
form = ResetForm()
return render_to_response(request, "reset.html")
get_form_class
metodą CBV , jeśli wiem, że muszę zrobić wiele rzeczy z żądaniem. Wielokrotne tworzenie klasy może wiązać się z pewnym narzutem, ale to po prostu przenosi ją z czasu importu do czasu wykonywania.
Odpowiedź Daniela Rosemana jest nadal najlepsza. Jednak użyłbym pierwszego argumentu pozycyjnego dla żądania zamiast argumentu słowa kluczowego z kilku powodów:
Na koniec użyłbym bardziej unikalnej nazwy, aby uniknąć zastępowania istniejącej zmiennej. Tak więc Moja zmodyfikowana odpowiedź wygląda następująco:
class MyForm(forms.Form):
def __init__(self, request, *args, **kwargs):
self._my_request = request
super(MyForm, self).__init__(*args, **kwargs)
def clean(self):
... access the request object via self._my_request ...
świeży ser od cheesebaker @ pypi: django-requestprovider
Mam inną odpowiedź na to pytanie, zgodnie z twoim wymaganiem, aby uzyskać dostęp do użytkownika do czystej metody formularza. Możesz tego spróbować. View.py
person=User.objects.get(id=person_id)
form=MyForm(request.POST,instance=person)
forms.py
def __init__(self,*arg,**kwargs):
self.instance=kwargs.get('instance',None)
if kwargs['instance'] is not None:
del kwargs['instance']
super(Myform, self).__init__(*args, **kwargs)
Teraz możesz uzyskać dostęp do self.instance w dowolnej czystej metodzie w form.py
Kiedy chcesz uzyskać do niego dostęp przez „przygotowane” widoki klas Django, CreateView
to jest taka mała sztuczka, którą trzeba znać (= oficjalne rozwiązanie nie działa po wyjęciu z pudełka). W swoim własnym CreateView
będziesz musiał dodać taki kod:
class MyCreateView(LoginRequiredMixin, CreateView):
form_class = MyOwnForm
template_name = 'my_sample_create.html'
def get_form_kwargs(self):
result = super().get_form_kwargs()
result['request'] = self.request
return result
= w skrócie jest to rozwiązanie, które należy przekazać request
do formularza z widokami tworzenia / aktualizacji Django.