Formun clean () yönteminde istek nesnesine veya başka bir değişkene nasıl erişebilirim?


99

Bir formun temiz yöntemi için request.user yapmaya çalışıyorum, ancak istek nesnesine nasıl erişebilirim? Değişken girdisine izin vermek için temiz yöntemi değiştirebilir miyim?

Yanıtlar:


157

Ber'in cevabı - bunu threadlocals'da saklamak - çok kötü bir fikir. Bunu bu şekilde yapmak için kesinlikle hiçbir neden yok.

Çok daha iyi bir yol, __init__ekstra bir anahtar kelime argümanı almak için formun yöntemini geçersiz kılmaktır request. Bu, isteği formda , gerektiği yerde ve temiz yönteminizde erişebileceğiniz yerden depolar .

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 ...

ve size göre:

myform = MyForm(request.POST, request=request)

4
Bu durumda haklısın. Ancak, bu eski Formlarda / Görünümlerde değişiklik yapılması istenmeyebilir. Ayrıca, yöntem parametreleri veya örnek değişkenleri eklemenin imkansız olduğu iş parçacığı yerel deposu için kullanım durumları vardır. Talep verilerine erişime ihtiyaç duyan bir sorgu filtresine çağrılabilir bağımsız değişken düşünün. Çağrıya bir parametre ekleyemezsiniz veya başvurulacak herhangi bir örnek yoktur.
Ber

4
Bir yönetici formunu genişlettiğinizde kullanışlı değildir, çünkü formunuzu istek var. Herhangi bir fikir?
Mordi

13
Neden iş parçacığı yerel depolamayı kullanmanın çok kötü bir fikir olduğunu söylüyorsunuz? İsteği her yere iletmek için kodu bırakmak zorunda kalmaz.
Michael Mior

9
Ben istek nesnesinin kendisini forma değil, ihtiyaç duyduğunuz istek alanlarını (yani kullanıcı) iletirim, aksi takdirde form mantığınızı istek / yanıt döngüsüne bağlayarak testi zorlaştırırsınız.
Andrew Ingram

2
Chris Pratt admin.ModelAdmin içinde formları ile uğraşırken için de iyi bir çözümü vardır
radtek

35

GÜNCELLENMİŞ 10/25/2011 : Şimdi bunu yöntem yerine dinamik olarak oluşturulmuş bir sınıfla kullanıyorum, çünkü Django 1.3 başka türlü bazı tuhaflıklar gösteriyor.

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

Ardından MyCustomForm.__init__aşağıdaki gibi geçersiz kılın :

class MyCustomForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyCustomForm, self).__init__(*args, **kwargs)

Daha sonra herhangi yönteminden istek nesnesi erişebilirsiniz ModelFormile self.request.


1
Chris, "def __init __ (self, request = None, * args, ** kwargs)" kötü, çünkü hem ilk konumsal argümanda hem de kwargs'ta istekle sonuçlanacak. Bunu "def __init __ (self, * args, ** kwargs)" olarak değiştirdim ve işe yarıyor.
slinkp

1
Hata. Bu sadece benim açımdan bir hataydı. Diğer güncellemeyi yaptığımda kodun bu kısmını güncellemeyi ihmal ettim. Yakaladığınız için teşekkürler. Güncellenmiş.
Chris Pratt

4
Bu gerçekten bir metasınıf mı? Sanırım bu normal bir şekilde geçersiz __new__kılınır, daha sonra sınıfın __init__yöntemine geçecek olan 's kwargs'a istek eklersiniz . Sınıfı adlandırmaktan ModelFormWithRequestçok daha net olduğunu düşünüyorum ModelFormMetaClass.
k4ml

2
Bu bir metasınıf içinde DEĞİLDİR! Stackoverflow.com/questions/100003/… sayfasına
frnhr

32

Değeri ne olursa olsun , işlev tabanlı görünümler yerine Sınıf Tabanlı Görünümler kullanıyorsanız get_form_kwargs, düzenleme görünümünüzde geçersiz kılın . Özel bir CreateView için örnek kod :

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

Yukarıdaki görünüm kodu request, formun __init__yapıcı işlevi için anahtar sözcük bağımsız değişkenlerinden biri olarak kullanılabilir hale getirecektir . Bu nedenle sizin işinizde 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)

1
Bu benim için çalıştı. Notu yazıyorum çünkü yine de karmaşık WizardForm mantığı nedeniyle get_form_kwargs kullanıyordum. WizardForm için başka bir cevap görmedim.
datakid

2
Benden başka biri bunun bir web çerçevesi için oldukça ilkel olan bir şeyi yapmak için büyük bir karmaşa olduğunu düşünüyor mu? Django harika ama bu CBV'yi hiç kullanmak istememe neden oluyor.
trpt4him

1
IMHO CBV'lerin faydaları, FBV'lerin dezavantajlarından çok daha ağır basar, özellikle de% 100 birim test kapsamını hedefleyen 25'ten fazla geliştiricinin kod yazdığı büyük bir projede çalışıyorsanız. Django'nun daha yeni sürümlerinin requestnesnenin get_form_kwargsotomatik olarak içinde olmasını sağlayıp sağlamadığından emin değilim .
Joseph Victor Zammit

Benzer şekilde, get_form_kwargs'daki nesne örneğinin kimliğine erişmenin herhangi bir yolu var mı?
Hassan Baig 18

1
@HassanBaig Muhtemelen kullanıyor self.get_object? CreateViewUzanır SingleObjectMixin. Ancak bunun işe yarayıp yaramayacağı veya bir istisnayı ortaya çıkarması, yeni bir nesne oluşturmanıza veya mevcut bir nesneyi güncellemenize bağlıdır; yani her iki durumu da test edin (ve tabii ki silme).
Joseph Victor Zammit

17

Genel yaklaşım, istek nesnesini bir ara yazılım kullanarak yerel bir evre başvurusunda depolamaktır. Ardından buna, Form.clean () yöntemi dahil olmak üzere uygulamanızın herhangi bir yerinden erişebilirsiniz.

Form.clean () yönteminin imzasını değiştirmek, Django'nun sizin istediğiniz, değiştirilmiş bir sürümüne sahip olduğunuz anlamına gelir.

Teşekkür ederim ara yazılım sayısı şuna benzer:

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

Bu ara yazılımı Django belgelerinde açıklandığı gibi kaydedin


2
Yukarıdaki yorumlara rağmen, bu yöntem işe yararken diğer yöntem işe yaramaz. Form nesnesinin bir öznitelik Ayar init güvenilir, temiz yöntemlere taşınmaz iplik yerli ayar oysa bu veriler üzerinde gerçekleştirilmesine imkan vermez.
rplevy

4
@rplevy, formun bir örneğini oluştururken istek nesnesini gerçekten ilettiniz mi? Fark etmediyseniz **kwargs, anahtar kelime argümanları kullandığını , bu da istek nesnesini olarak iletmeniz gerektiği anlamına gelir MyForm(request.POST, request=request).
57'de değiştirildi

13

Django yöneticisi için, Django 1.8'de

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

1
Yukarıda en yüksek puan alan yöntem gerçekten de Django 1.6 ve 1.9 arasında bir yerde çalışmayı durdurmuş gibi görünüyor. Bu işe yarıyor ve çok daha kısa. Teşekkürler!
Raik

9

Yöneticiyi özelleştirirken bu sorunla karşılaştım. Belirli bir alanın, belirli yöneticinin kimlik bilgilerine göre doğrulanmasını istedim.

İsteği forma bir argüman olarak iletmek için görünümü değiştirmek istemediğim için, şunu yaptım:

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

Bunun için teşekkürler. Hızlı yazım hatası: 11. satırda obj=objdeğil obj=None.
François Constant

Gerçekten güzel cevap, onu seviyorum!
Luke Dupin

Django 1.9 sunar: 'function' object has no attribute 'base_fields'. Ancak, daha basit (sonuçsuz) @ François cevabı sorunsuz çalışıyor.
raratiru

5

Bu yöntemi her zaman kullanamazsınız (ve muhtemelen kötü uygulamasıdır), ancak formu yalnızca tek bir görünümde kullanıyorsanız, onu görünüm yönteminin kendi içinde kapsama alabilirsiniz.

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")

Bu bazen gerçekten güzel bir çözümdür: İstekle get_form_classbirçok şey yapmam gerektiğini biliyorsam, bunu genellikle bir CBV yönteminde yapıyorum. Sınıfı tekrar tekrar oluşturmanın bazı ek yükleri olabilir, ancak bu onu içeri aktarma zamanından çalışma zamanına taşır.
Matthew Schinckel

5

Daniel Roseman'ın cevabı hala en iyisidir. Ancak, birkaç nedenden ötürü anahtar kelime argümanı yerine istek için ilk konumsal argümanı kullanırdım:

  1. Aynı isimli bir kwarg'ı geçersiz kılma riskiniz yoktur.
  2. Talep isteğe bağlıdır ve bu doğru değildir. Bu bağlamda istek özniteliği hiçbir zaman Yok olmamalıdır.
  3. Değiştirmek zorunda kalmadan arg'leri ve kwarg'ları ebeveyn sınıfa geçirebilirsiniz.

Son olarak, mevcut bir değişkeni geçersiz kılmaktan kaçınmak için daha benzersiz bir ad kullanacağım. Böylece, değiştirilmiş cevabım şöyle görünür:

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 ...


3

Gereksiniminize göre bu soruya başka bir cevabım var, kullanıcıya formun temiz yöntemine erişmek istersiniz. Bunu deneyebilirsiniz. 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)

Artık self.instance'e form.py'deki herhangi bir temiz yöntemle erişebilirsiniz.


0

Buna CreateView, bilinmesi gereken küçük bir numara varmış gibi "hazırlanmış" Django sınıfı görünümleri aracılığıyla erişmek istediğinizde (= resmi çözüm kutudan çıkmaz). Kendi başınıza aşağıdaki CreateView gibi bir kod eklemeniz gerekecek:

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

= kısaca bu, requestDjango'nun Oluştur / Güncelle görünümleri ile formunuza geçmenin çözümüdür .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.