Как стать автором
Обновить

Комментарии 45

Создание модели User не означает, что автоматом создастся связанная модель Profile. Зато при обращении к get_profile() для вновь созданного юзера вылетит исключение — вот тут сомневаться не приходится.
А я вот сомневался :-[

Спасибо, годная штука судя по описанию.
НЛО прилетело и опубликовало эту надпись здесь
Верно для request.user, но не работает для десятка comment.user.get_profile() в списке комментов.
Да, профиль кешируется в инстансе User, но есть одно «но»: инстансыэти, хоть и одинаковые, являются лишь друг друга :)

>>> posts = Post.objects.filter(user__pk=2).order_by('?')[:2]
>>> assert posts[0] != posts[1]
>>> assert posts[0].user == posts[1].user
>>> assert posts[0].user is posts[1].user
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AssertionError


Как видите, инстансы разные, соотвественно, кеш не сработает
НЛО прилетело и опубликовало эту надпись здесь
Можно, только осторожно.

Если Вы знаете, как работают все приложения, которые используете у себя и уверены, что ни одно из них не обратится к User.username, называйте его, как хотите, хоть imya_polzovatelya. Или вообще убирайте, пусть будет аутентификация по имейлу. Единственное только, придётся собственные auth-быкенды писать.

Но как по мне, дефолтный набор полей у Примата довольно универсален. Гораздо более интересна возможность создавать свои поля :)
Профайлы через наследование и манки-патчинг — жуть, конечно. Но через OneToOne вот меня профайлы не парили никогда.

а) у человека ведь часто много профайлов, от разных приложений; сложность и дополнительные зависимости как раз создадутся, если все приложения должны будут использовать 1 модель — какие-то миграции делать туда-сюда при подключении/отключении приложений, например;
б) чтоб создавать все автоматом — есть AutoOneToOne;
в) насчет select_related — неправда, он работает для OneToOne в обе стороны уже года полтора как.

AUTH_PROFILE_MODULE = 'accounts.Profile' — штука необязательная и чуть ли не вредная, как и метод get_profile. Иногда удобно, но обычно просто путает. Проще явно user.profile написать уж, если нужно.

А с django-primate все как-то непонятно становится сразу, больно уж на хак смахивает, особенно убирание полей first_name и last_name, на которые вообще-то сторонний код завязан может быть, и манки-патчинг админки. Это, по сути, метод с манки-патчингом модели, доведенный до логического завершения. Это должно быть удобнее OneToOne при написании формы редактирования профайла, но в остальном преимуществ не вижу, с осторожностью лучше к таким штукам относиться.

Автор sorl-thumbnails вообще странный чувак бывает иногда — убрал как-то старый sorl-thumbnails из репозитория на гуглокоде и с pypi, а на pypi выложил переписанную версию (с ненужными в большинстве случаев наворотами), которая обратно со старой не совместима. Но это так, лирика)
Относительно проблематики monkey patching-а — мне тоже кажется, что это очень непрозрачно. Пришел в проект новый разработчик, смотрит на contrib.auth.models.User, а у него first_name нету. Мне бы не пришло в голову искать причину этому в manage.py.
Такие штуки принято документировать и писать тесты, которые враз скажут, что откуда и куда.
Я сделал допущение, что профиль только один. Если профилей по каким-то причинам должно быть несколько, то да, конечно, правильнее их сделать отдельно от самого пользователя. Но у меня таких случаев пока не бывало.

Поле AutoOneToOne штука хорошая. Но нестандартная :) Обратно, всякие get_profile и AUTH_PROFILE_MODEL предусмотрены создателями джанги и даже вроде как ими рекомендуются. Наверное, поэтому многие, в особенности новички, предпочитают идти именно вторым путём: он описан в мануале и ничего не надо ставить.

Примат действительно является манки-патчингом, если так можно выразиться, эволюционировавшим до человека :) Но всё-таки это магия, о чём я и написал. Использовать её надо с осторожностью. Но тем, кто пишет тесты, особо бояться нечего.
Кстати, насчёт тестов, совсем забыл: с приматом некоторые родные джанговские тесты на contrib.auth поломаются. Например, на неуникальность email. Надо об этом помнить, если кто решится исопльзовать.
AUTH_PROFILE_MODULE получается в таком контексте вообще бессмысленна. Такое связывание, не избавит от одного лишнего запроса, от «ручного» кодирования не избавляет.
Так и есть, штука бессмысленная. На нее в рассылке уже ругались, но до того, чтоб объявить deprecated или документацию по-другому написать, руки так и не дошли ни у кого.
> в) насчет select_related — неправда, он работает для OneToOne в обе стороны уже года полтора как.
А как он работает в обратную сторону? Действительно, интересно. Буквально на днях я поэкспериментировал: если передавать список объектов с FK-полем в шаблон и выводить их и объекты, на которые ссылается FK-поле, я получал 3 запроса к БД. Если же передавать список объектов, а на них ссылающиеся с FK-полем доставать через Instance.entry_set.all, то запросов к БД получается много.
НЛО прилетело и опубликовало эту надпись здесь
Если я правильно понимаю, в обратную сторону оно работает только с полями OneToOneFields
НЛО прилетело и опубликовало эту надпись здесь
Eже успел написать свою модельку профайла (наследование), но под обезьяну думаю переделаю, выглядит удобнее. Спасибо за примата, обязательно попробую.
Пожалуйста :)

Но имейте в виду, что этот пост — не джинса django-primate. С магией надо быть очень осторожным. Если не пишете тесты, будет хорошей идеей начать это делать.
очевидно, что django.contrib.auth писали какие-то панки. но так делать тоже нельзя — ломающиеся джанговские тесты являются прямым тому доказательством.
модель пользователя django-primate имеет следующие отличия от auth.User… В остальном же модель пользователя primate гуляет как утка, плавает как утка и крякает как утка ведёт себя очень похоже на джанговский первоисточник.

С одной стороны, неплохо было бы в примате пропатчить и тесты джанги, чтобы они хотя бы не ломались. Но тут вопрос скользкий: примат не накладывает ограничений на схему новой модели пользователей, поэтому тесты, написанные для дефолтной приматовской модели всё равно оказались бы бесполезными, если в проекте модель пользователя будет изменена.

Думаю, лучше полагаться на разработчика проекта: пусть сам пишет тесты согласно своим требованиям. Или не пишет, но тогда он сам себе злобный Буратино.
С одной стороны, неплохо было бы в примате пропатчить и тесты джанги, чтобы они хотя бы не ломались.
патчить тесты не нужно ни в коем случае, ведь тесты отражают принятые соглашения. если новая реализация ломает тесты, это означает лишь одно — реализация написана с ошибками (не важно: намеренными или нет).
Давайте назовём это чуточку политкорректнее: дефолтный функционал реализации отличается от исходного :) Сами же согласились, что django.contrib.auth наркоманский. Патч в общем случае делает его гибче. Никто ж, в конце концов, не мешает использовать модель User без уникального индекса на email и с полями first_name и last_name. Тогда и тесты не сломаются.
Я раньше тоже извращался и с манкипатчингом и с наследованием. Сейчас мне всё это кажется созданием проблем на ровном месте. Я просто создаю дополнительную модель с нужными полями и связываю её через OneToOneField, даже без префикса Auto, просто создаю по сигналу эту модель. Считаю это самым простым и прозрачным способом.
А я вот как-то наоборот постарался уйти и от ненужных сигналов, и от того, что часть информации хранится в User, а часть — User.profile. Как по мне, это чертовски неудобно, по крайней мере, если у пользователя может быть только один профиль.

Видимо, каждому своё :)
Чем вам не угодил подход с отдельной моделью профиля? Не понимаю, почему вдруг один из ключевых принципов теории реляционых баз данных стал костылем? УМВР, не было проблем с профилями пользователей.

По вашим пунктам:

> сложность поддержки

Запутаться??? Логин/пароль и допустим сексуальная ориентация пользователя — это же совершенно разные данные, умоляю, почему они все должны храниться в одном объекте? К тому же, что делать, если у меня вообще три разных типа профилей для пользователей, и в каждом свои данные?

> нецелевая растрата ресурсов

А не будет ли растратой ресурсов то, что я каждый раз подгружаю совершенно не нужные мне данные чтобы получить, допустим, список пользователей? (конечно даже если не указывать конкретные поля в запросе, django может быть оптимизирует всё что нужно, и лишние данные не будут передаваться, но всё равно семантика нарушается, и это всё уже зависит от того как напишешь)

> а ещё всё надо руками делать!

Ну вообще это нормально, не нужен мне профиль для какого-то пользователя — не буду создавать.

В общем не убедили вы меня в необходимости использования столь жесткого манкипатчинга в решении данной проблемы, но за статью про эту библиотеку всё равно спасибо — больше костылей, хороших и разных!
Чем мне не угодил подход с отдельной моделью я уже написал: две сущности там, где нужна одна (если нужен один тип профиля. Для разных типов же такой способ считаю наиболее верным).

Логин/пароль и допустим сексуальная ориентация пользователя — это же совершенно разные данные, умоляю, почему они все должны храниться в одном объекте?
Окей. А имя и фамилия где должны храниться — там же, где пароль, или рядом с сексуальной ориентацией? :-)

А не будет ли растратой ресурсов то, что я каждый раз подгружаю совершенно не нужные мне данные чтобы получить, допустим, список пользователей?
У меня для Вас плохие новости: джанговский ORM загружает модели полностью. Даже если Вы обращаетесь к username пользователя, сервер БД передаст и фамилию, и имя, и пароль (захешированный, конечно) и пресловутую сексуальную ориентацию.

А имя и фамилия где должны храниться — там же, где пароль, или рядом с сексуальной ориентацией? :-)

… ответ на этот вопрос дает нормализация. ;)
У меня для Вас плохие новости: джанговский ORM загружает модели полностью. Даже если Вы обращаетесь к username пользователя, сервер БД передаст и фамилию, и имя, и пароль (захешированный, конечно) и пресловутую сексуальную ориентацию.


Ну вот же! А ведь если разбить эти данные на несколько моделей, то мы получим возможность не передавать лишние. Лучше 20 мелких запросов, чем 10 но в 3 раза больших.
Я не очень понимаю, зачем плодить число запросов там, где можно этого не делать. Основное время занимает выполнение запроса, а не передача данных, разве нет? Или Вы хотите внутрисетевой (обычно) трафик экономить?
зачем плодить число запросов там, где можно этого не делать

Это замечание справедливо тогда, когда вы пытаетесь выполнить необходимую работу скриптом вместо того чтобы предоставить её базе данных. Здесь же, нагрузка от обработки лишнего объема данных (в данной ситуации 1/3 объема — лишние, а на практике обычно в таких ситуациях всё гораздо хуже), превзойдет нагрузку от обработки большего числа запросов. Хотя это конечно зависит от базы данных, какой-нибудь MySQL с каким-нибудь движком с кривыми транзациями может вполне и загнуться от кучи мелких запросов… Но опять же, эти 20 запросов прекрасно превращаются в 10, с тем же объемом данных, если грамотно подойти к делу. А чтобы вытащить тот же объем из лишнего базе придется проделать бОльшую работу. Как уже говорили здесь, нормализация рулит.

А трафик в большистве случаев вообще локальный. И действительно, в большинстве случаев может не учитываться… Но это в большинстве. А меньшинство составляют Highload-проекты, где снижение трафика на сервере баз данных на 1/3 это существенный профит, ради которого разработчики не то что модели перелопатить, но и новую СУБД могут написать.
Здесь же, нагрузка от обработки лишнего объема данных… превзойдет нагрузку от обработки большего числа запросов.
Откуда такие выводы? Измерения проводились?

Но опять же, эти 20 запросов прекрасно превращаются в 10, с тем же объемом данных, если грамотно подойти к делу. А чтобы вытащить тот же объем из лишнего базе придется проделать бОльшую работу. Как уже говорили здесь, нормализация рулит.
Не поспеваю за Вашей мыслью. Что значит «грамотно подойти к делу»? И при чём тут нормализация?
две сущности там, где нужна одна


Нет, здесь нужны две — auth.User начинается на auth, что как бы намекает. А myapp.Profile начинается на myapp. Не нужно смешивать независимые друг от друга данные без необходимости.
Не нужно смешивать независимые друг от друга данные без необходимости.
Они первые придумали засунуть необязательные поля first_name и last_name в модель, которая по идее должна хранить только аутентификационную информацию.
first_name и last_name нужны непосредственно для аутентификации. По крайней мере, хотя бы для того чтобы вывести «Здравствуйте, {{ first_name }} {{ last_name }}, вы успешно аутентифицировались». Это данные, идентифицирующие пользователя. Ну и да, куда их засунуть, если не сюда?
«Здравствуйте, {{ first_name }} {{ last_name }}, вы успешно аутентифицировались. А ваша сексуальная ориентация — {{ orientation }}». Не?
Не, orientation будет в другом шаблоне, где-нибудь справа в отдельном блоке, не зависимо от аутентификации.
Это было [irony][/irony]. На мой взгляд, имя и фамилия к аутентификации относятся ничуть не больше той же пресловутой ориентации. И неважно где её выводить
И почему бы не дать профиль пользователю, у которого ещё нет своего auth.User?
Например, потому что его, пользователя, просто не существует? =)
Чёрт, не дописал самое главное.

Я ни разу не убеждаю использовать primate. Это магия и с ней надо быть осторожным. Я бы, пожалуй, рекомендовал прислушаться к совету   kmike и наследовался модели, хоть это тоже не лишено некоторых рутинных дейтсвий, от которых избавляет примат. Или не позволяет выкинуть нежелательные поля.

С другой стороны, дополнительная с прикреплённым к и без того не аскетичному User у меня вызывает неприязнь. Ещё раз повторюсь, что речь идёт о случаях, когда нужен всего один тип профиля. У меня других не было, если что.
kmike тоже советует использовать отдельную модель для профиля…

Не важно один тип профиля или несколько. Важна производительность и удобство работы. Если в какой-то ситуации действительно удобно обращаться ко всем данным через одну модель и это не вызывает существенного спада производительности — можно использовать primate, как гарантию того, что вы не наткнетесь через манкипатчинг на какие-то грабли самостоятельно.

Я бы как раз позиционировал эту библиотеку как попытку избавить молодых джангистов от желания самостоятельно лезть в чужие структуры данных без особого на то повода.
Если я правильно понял kmike, он советует использовать отдельную модель для каждого профиля, в случае когда их, профилей, несколько. Я же говорю про случае с единственным профилем, бо у меня просто по-другому не было.

Я считаю, что удобство — это когда есть одна форма и одна модель. И когда не нужно при редактировании Profile во вьюшке обновлять связанный с Profile объект User, получая из form.cleaned_data значения, которые нужно охранить у пользователя. Как-то так :)
А почему не освещен еще один вариант предлагаемый джангой — использование прокси класса:

class ExtUser(User):
'''
Расширение стандартного класса пользователей
'''

class Meta:
proxy = True


Внутри можно переопределять любой из метода, но поля не добавить, это только через профайл.
Пруфлинк: docs.djangoproject.com/en/1.3/topics/db/models/#proxy-models
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории