2011-01-27 10 views
17

Ho il seguente modulo. Come posso ricontrollare la password da parte dell'utente, prima che l'utente possa finalmente cambiare il suo indirizzo email. Anche lui ha effettuato l'accesso, voglio solo essere sicuro che sia davvero l'utente. Solo una cosa sicura.check_password() da un utente di nuovo

Come faccio a farlo con .check_password()?

'EmailChangeForm' object has no attribute 'user' 

    /home/craphunter/workspace/project/trunk/project/auth/user/email_change/forms.py in clean_password, line 43 

from django import forms 
from django.db.models.loading import cache 
from django.utils.translation import ugettext_lazy as _ 
from django.contrib.auth.models import User 


class EmailChangeForm(forms.Form): 

    email = forms.EmailField(label='New E-mail', max_length=75) 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, user, *args, **kwargs): 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 
     self.user = user 

    def clean_password(self): 
     valid = self.user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 
     return valid 

    def __init__(self, username=None, *args, **kwargs): 
     """Constructor. 

     **Mandatory arguments** 

     ``username`` 
      The username of the user that requested the email change. 

     """ 
     self.username = username 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_email(self): 
     """Checks whether the new email address differs from the user's current 
     email address. 

     """ 
     email = self.cleaned_data.get('email') 

     User = cache.get_model('auth', 'User') 
     user = User.objects.get(username__exact=self.username) 

     # Check if the new email address differs from the current email address. 
     if user.email == email: 
      raise forms.ValidationError('New email address cannot be the same \ 
       as your current email address') 

     return email 

risposta

17

avrei refactoring del codice per essere simile a questo:

Vista:

@login_required 
def view(request, extra_context=None, ...): 

    form = EmailChangeForm(user=request.user, data=request.POST or None) 

    if request.POST and form.is_valid(): 
     send_email_change_request(request.user, 
            form.cleaned_data['email'], 
            https=request.is_secure()) 
     return redirect(success_url) 
    ... 

convalida password va a forma: la logica

class EmailChangeForm(Form): 
    email = ... 
    old_password = CharField(..., widget=Password()) 

    def __init__(self, user, data=None): 
     self.user = user 
     super(EmailChangeForm, self).__init__(data=data) 

    def clean_old_password(self): 
     password = self.cleaned_data.get('password', None) 
     if not self.user.check_password(password): 
      raise ValidationError('Invalid password') 

Estratto dalla vista:

def send_email_change_request(user, new_email, https=True): 

    site = cache.get_model('sites', 'Site') 

    email = new_email 
    verification_key = generate_key(user, email) 

    current_site = Site.objects.get_current() 
    site_name = current_site.name 
    domain = current_site.domain 

    protocol = 'https' if https else 'http' 

    # First clean all email change requests made by this user 
    qs = EmailChangeRequest.objects.filter(user=request.user) 
    qs.delete() 

    # Create an email change request 
    change_request = EmailChangeRequest(
     user = request.user, 
     verification_key = verification_key, 
     email = email 
    ) 
    change_request.save() 

    # Prepare context 
    c = { 
     'email': email, 
     'site_domain': 'dev.tolisto.de', 
     'site_name': 'tolisto', 
     'user': self.user, 
     'verification_key': verification_key, 
     'protocol': protocol, 
    } 
    c.update(extra_context) 
    context = Context(c) 

    # Send success email 
    subject = "Subject" # I don't think that using template for 
         # subject is good idea 
    message = render_to_string(email_message_template_name, context_instance=context) 

    send_mail(subject, message, None, [email]) 

Non mettere cose complicate nelle viste (come il rendering e l'invio di e-mail).

+0

Grazie mille. Anche aiutato! – craphunter

9

Mi sento come hai risposto alla tua domanda:)

La documentazione sul metodo check_password sono qui: http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.check_password

success = user.check_password(request.POST['submitted_password']) 
if success: 
    # do your email changing magic 
else: 
    return http.HttpResponse("Your password is incorrect") 
    # or more appropriately your template with errors 

Dal momento che si sta già passando in request.user il tuo costruttore di moduli (sembra che tu abbia sopravvalutato __init__ per motivi personali) potresti mettere tutta la tua logica nel modulo senza problemi.

class MyForm(forms.Form): 
    # ... 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, user, *args, **kwargs): 
      super(MyForm, self).__init__(*args, **kwargs) 
      self.user = user 

    def clean_password(self): 
     valid = self.user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 
     return valid 

aggiornamento dopo aver visto i moduli

OK. Il problema principale è che __init__ è stato definito due volte, rendendo inutile la prima istruzione. Il secondo problema che vedo è che faremmo più query per user quando davvero non dovremmo.

Ci siamo allontanati un po 'dalla tua domanda iniziale, ma si spera che questa sia un'esperienza di apprendimento.

ho cambiato solo poche cose:

  • Rimossa la definizione extra __init__
  • Modificato __init__ ad accettare un'istanza User invece di un testo username
  • rimosso la query per User.objects.get(username=username) dal momento che' rientrando in un oggetto utente.

basta ricordarsi di passare il modulo costruttore user=request.user invece di username=request.user.username

class EmailChangeForm(forms.Form): 
    email = forms.EmailField(label='New E-mail', max_length=75) 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, user=None, *args, **kwargs): 
     self.user = user 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_password(self): 
     valid = self.user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 

    def clean_email(self): 
     email = self.cleaned_data.get('email') 

     # no need to query a user object if we're passing it in anyways. 
     user = self.user 

     # Check if the new email address differs from the current email address. 
     if user.email == email: 
      raise forms.ValidationError('New email address cannot be the same \ 
       as your current email address') 

     return email 

Infine dal momento che stiamo parlando di buona pratica qui, io consiglierei di seguire attraverso con Skirmantas suggerimenti circa lo spostamento il tuo codice di visualizzazione corrente per un metodo di modulo in modo da poter chiamare semplicemente myform.send_confirmation_email.

Suona come un buon esercizio!

+0

Grazie amico! Sì, sono una newbee in django ... a volte la soluzione è così semplice. – craphunter

+0

Nessun problema! Sì, amo il django e il pitone. Oh, sai cosa? Per semplificare, mi viene in mente che dovresti semplicemente aggiungere un campo password al modulo poiché stai già passando nell'oggetto utente. Basta definire 'password = forms.CharField (widget = forms.PasswordInput})' e sovrascrivere 'clean_password' per eseguire il controllo della password. In questo modo puoi rimanere nel framework dei moduli per rivisualizzare il modulo di errore. –

+0

Cosa intendi con clean_password? C'è un errore di battitura nella password})? – craphunter

1

grazie ancora a Yuji. Funziona quando non ho nel mio primo l'utente variabile. Ho anche aggiunto in def clean_password le prime 2 righe dal def clean_email

from django import forms 
from django.db.models.loading import cache 
from django.utils.translation import ugettext_lazy as _ 
from django.contrib.auth.models import User 


class EmailChangeForm(forms.Form): 

    email = forms.EmailField(label='New E-mail', max_length=75) 
    password = forms.CharField(widget=forms.PasswordInput) 

    def __init__(self, *args, **kwargs): 
     self.user = user 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_password(self): 
     User = cache.get_model('auth', 'User') 
     user = User.objects.get(username__exact=self.username) 
     valid = user.check_password(self.cleaned_data['password']) 
     if not valid: 
      raise forms.ValidationError("Password Incorrect") 
     return valid 

    def __init__(self, username=None, *args, **kwargs): 
     """Constructor. 

     **Mandatory arguments** 

     ``username`` 
      The username of the user that requested the email change. 

     """ 
     self.username = username 
     super(EmailChangeForm, self).__init__(*args, **kwargs) 

    def clean_email(self): 
     """Checks whether the new email address differs from the user's current 
     email address. 

     """ 
     email = self.cleaned_data.get('email') 

     User = cache.get_model('auth', 'User') 
     user = User.objects.get(username__exact=self.username) 

     # Check if the new email address differs from the current email address. 
     if user.email == email: 
      raise forms.ValidationError('New email address cannot be the same \ 
       as your current email address') 

     return email 
+0

Complimenti che questo funziona! Comunque dai un'occhiata alla mia risposta aggiornata dato che stai facendo 2 query per Utente quando potrebbe essere None! Potrebbe essere un buon esercizio :) –

+0

Grazie ancora a Portland. Ho fatto il tuo consiglio e sta funzionando! Vado a letto! In Germania sono le 2 del mattino! Buona serata! – craphunter

Problemi correlati