Pull to refresh

Comments 11

Не все Django-проекты это требуют. Но да, согласен — ссылка точно уместна.
Кстати, метод get_cookies можно упростить:

def get_cookies(self, request):
    if request is None:
        return {}
            
    return {key: 'secret' for key in request.COOKIES}

Спасибо Dreadatour!

Принцип с else общий — если в конце блока if есть return, то всё то, что должно выполняться в случае, если условие if не расценивается как истинное, можно помещать не в блок else, а просто размещать сразу после блока if, без каких-либо блоков — потому что если условие if было расценено как истинное, то всё, что ниже, всё равно уже никогда не выполнится (даже если было вызвано исключение).
Мне, кажется, это только наоборот усложняет восприятие программы. Конструкция if-else-then визуально воспринимается проще и логика более простая, чем в «упрощённом» варианте.
Просто представьте, что у вас не одно условие для return, а хотя бы три, что бывает довольно часто.
Представить можно много чего, мы ведь говорим про конкретное упрощение кода, представленного выше, а не про все на свете варианты. Я высказал мысль о том, как бы я записал конкретный код, рассмотренный выше, а не абстрактный.
Я вообще не спорю, просто захотелось мыслями поделиться. Для меня KISS — тот вариант, про который я пишу, с явным else
За пост спасибо!

Но при авторизации скрытие email грозит, тем что можно долго искать проблему с дублированием email в другом регистре, что по де-фолту для джанги актуально. Во-первых, в стандартной модели пользователя email не уникальный. Во-вторых в стандратном поле EmailField нормализируется только домен. Таким образом, если вы все использовали из коробки и при регистрации сами не валидилировали email на уникальность с учетом возможного использования разного регистра символов то в зависимости от вашего бэкэнда утентификации при авторизации могут быть ошибки из-за того что в базе есть разные пользователи с адресами aruseni.magiku@gmail.com и Aruseni.Magiku@gmail.com. На рабочем сайте с большой базой пользователей вы можете долго искать эту проблему. Знание же для какого именно пользователя проблема актуальна в данном случае позволяет установить ее причину гораздо быстрее.
Спасибо, что подняли данную тему. Такая проблема действительно существует, если специально не позаботиться об обработке адресов электронной почты, набранных с использованием букв в разном регистре.

Реально регистрация может быть, например, такой (обработка IntegrityError тут для маловероятной ситуации race condition в случае одновременной регистрации двух пользователей):

Скрытый текст
if request.method == "POST":
    form = RegistrationForm(request.POST, request.FILES)
    if form.is_valid():
        email = form.cleaned_data["email"].lower()
        password = form.cleaned_data["password"]
        try:
            user = User.objects.create_user(hashlib.sha1(email).hexdigest()[:30],
                                       email,
                                       password)
            user.username = "".join(["user", str(user.id)])
            user.save()
        except IntegrityError:
            messages.error(request, u"При регистрации возникла ошибка. Пожалуйста, попробуйте ещё раз.")
            try:
                # Rollback the transaction (otherwise a DatabaseError would be raised when trying to delete the user)
                connection._rollback()
                # In case the IntegrityError was raised when renaming the user
                user.delete()
            # UnboundLocalError can be raised if the user variable is not defined
            except UnboundLocalError:
                pass
            return HttpResponseRedirect(reverse('dating.views.index'))
        UserProfile.objects.create(user=user,
                                   name=form.cleaned_data["name"],
                                   photo=form.cleaned_data["photo"])

        user = authenticate(username=user.username, password=password)
        login(request, user)

        email_body = render_to_string('registration_email.txt', {
            "username": form.cleaned_data["name"], "user_id": user.id
        })
        send_mail('Background Dating',
                  email_body,
                  None,
                  [email],
                  fail_silently=True)

        return HttpResponseRedirect(reverse('dating.views.index'))
else:
    form = RegistrationForm()

return render_to_response('index.html',
                          {"registration_form": form},
                          context_instance=RequestContext(request))

Форма регистрации с её родительским классом:

class UserProfileForm(forms.ModelForm):
    def clean_photo(self):
        image = self.cleaned_data.get('photo')
        if image:
            if not "." in image.name:
                raise ValidationError(u"Пожалуйста, убедитесь, что файл имеет расширение.")

            extension = image.name.rsplit('.',1)[1].lower()

            if extension not in ["jpg", "jpeg", "gif", "png"]:
                raise ValidationError(u"Пожалуйста, убедитесь, что изображение имеет одно из следующих расширений: jpg, jpeg, png, gif.")

            if image._size > 1*1024*1024:
                raise ValidationError(u"Пожалуйста, убедитесь, что размер изображения не больше 1 MiB.")

            im = Image.open(StringIO.StringIO(image.read()))

            # A dictionary of expected file extensions for each format (as returned by PIL)
            expected_file_extensions = {"JPEG": ["jpg", "jpeg"], "PNG": ["png"], "GIF": ["gif"]}

            if im.format not in expected_file_extensions:
                raise ValidationError(u"Пожалуйста, убедитесь, что изображение имеет один из следующих форматов: JPEG, PNG, GIF.")

            if extension not in expected_file_extensions[im.format]:
                raise ValidationError(u"Пожалуйста, убедитесь, что формат изображения соответствует его расширению.")

            if (im.size[0] < 300 or im.size[1] < 200):
                raise ValidationError(u"Пожалуйста, убедитесь, что разрешение фотографии не меньше 300×200.")

            if (max(im.size) > 10000):
                raise ValidationError(u"Пожалуйста, убедитесь, что разрешение фотографии не больше 10000×10000.")

            return image
        else:
            raise ValidationError(u"При получении изображения возникла ошибка")

    class Meta:
        model = UserProfile

class RegistrationForm(UserProfileForm):
    email = forms.EmailField(max_length=75)
    password = forms.CharField(max_length=128)

    def clean_email(self):
        email = self.cleaned_data['email'].lower()
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError(u"Пользователь с таким адресом электронной почты уже есть на сайте.")

        return email

    class Meta:
        model = UserProfile
        exclude = ('user',)

Соответственно, в бэкэнде авторизации просто позволяем пользователю вводить email с использованием букв любого регистра:

class EmailAuthBackend(ModelBackend):
    def authenticate(self, email=None, password=None, **kwargs):
        if not email:
            return None

        try:
            user = User.objects.get(email__iexact=email)
        except User.DoesNotExist:
            return None
        except User.MultipleObjectsReturned:
            user = User.objects.filter(email__iexact=email)[0]

        if user.check_password(password):
            return user


К счастью, в Django 1.5 обязательность танцев с бубном заметно уменьшится в связи с появлением определяемых разработчиком моделей User.
Sign up to leave a comment.

Articles