Réponses:
La réponse de Ber - la stocker dans des threadlocals - est une très mauvaise idée. Il n'y a absolument aucune raison de le faire de cette façon.
Une bien meilleure façon est de passer outre de la forme __init__
de méthode pour prendre un argument mot - clé supplémentaire, request
. Cela stocke la demande dans le formulaire , où elle est requise et à partir de laquelle vous pouvez y accéder dans votre méthode propre.
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 ...
et à votre avis:
myform = MyForm(request.POST, request=request)
MISE À JOUR 25/10/2011 : J'utilise maintenant ceci avec une classe créée dynamiquement au lieu d'une méthode, car Django 1.3 affiche une certaine bizarrerie autrement.
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
Remplacez ensuite MyCustomForm.__init__
comme suit:
class MyCustomForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(MyCustomForm, self).__init__(*args, **kwargs)
Vous pouvez ensuite accéder à l'objet de requête à partir de n'importe quelle méthode de ModelForm
avec self.request
.
__new__
kwargs de la classe qui plus tard sera transmise à la __init__
méthode de la classe . Nommer la classe ModelFormWithRequest
je pense que c'est beaucoup plus clair dans sa signification que ModelFormMetaClass
.
Pour ce que cela vaut, si vous utilisez des vues basées sur des classes , au lieu de vues basées sur des fonctions, remplacez-les get_form_kwargs
dans votre vue d'édition. Exemple de code pour un CreateView personnalisé :
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
Le code de vue ci-dessus rendra request
disponible comme l'un des arguments de mot-clé de la __init__
fonction constructeur du formulaire . Par conséquent dans votre ModelForm
faire:
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
objet get_form_kwargs
automatiquement.
self.get_object
? Le CreateView
prolonge le SingleObjectMixin
. Mais si cela fonctionne ou déclenche une exception, cela dépend si vous créez un nouvel objet ou mettez à jour un objet existant; c'est à dire tester les deux cas (et suppression bien sûr).
L'approche habituelle est de stocker l'objet de requête dans une référence locale de thread à l'aide d'un middleware. Ensuite, vous pouvez y accéder depuis n'importe où dans votre application, y compris la méthode Form.clean ().
Changer la signature de la méthode Form.clean () signifie que vous avez votre propre version modifiée de Django, ce qui n'est peut-être pas ce que vous voulez.
Merci, le nombre de middleware ressemble à ceci:
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
Enregistrez ce middleware comme décrit dans la documentation Django
**kwargs
, ce qui signifie que vous devrez transmettre l'objet de requête en tant que MyForm(request.POST, request=request)
.
Pour l'administrateur Django, dans 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
J'ai rencontré ce problème particulier lors de la personnalisation de l'administrateur. Je voulais qu'un certain champ soit validé en fonction des informations d'identification de l'administrateur particulier.
Puisque je ne voulais pas modifier la vue pour transmettre la requête en tant qu'argument au formulaire, voici ce que j'ai fait:
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
pas obj=None
sur la ligne 11.
'function' object has no attribute 'base_fields'
. Cependant, la réponse la plus simple (sans fermeture) @ François fonctionne bien.
Vous ne pouvez pas toujours utiliser cette méthode (et c'est probablement une mauvaise pratique), mais si vous n'utilisez le formulaire que dans une vue, vous pouvez l'étendre à l'intérieur de la méthode de vue elle-même.
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
méthode CBV , si je sais que j'ai besoin de faire beaucoup de choses avec la requête. La création répétée de la classe peut entraîner une surcharge, mais cela ne fait que la déplacer du moment de l'importation au moment de l'exécution.
La réponse de Daniel Roseman est toujours la meilleure. Cependant, j'utiliserais le premier argument positionnel pour la requête au lieu de l'argument mot-clé pour plusieurs raisons:
Enfin, j'utiliserais un nom plus unique pour éviter de remplacer une variable existante. Ainsi, ma réponse modifiée ressemble à:
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 ...
fromage frais du fromager @ pypi: django-requestprovider
J'ai une autre réponse à cette question selon votre condition que vous souhaitez accéder à l'utilisateur dans la méthode propre du formulaire. Vous pouvez essayer ceci. 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)
Vous pouvez maintenant accéder à self.instance dans n'importe quelle méthode propre dans form.py
Lorsque vous voulez y accéder via des vues de classe Django "préparées", comme CreateView
il y a une petite astuce à connaître (= la solution officielle ne fonctionne pas hors de la boîte). Dans le vôtre, CreateView
vous devrez ajouter un code comme celui-ci:
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
= en bref c'est la solution à passer request
à votre formulaire avec les vues Créer / Mettre à jour de Django.