User Profiles: Pros, Cons, Pitfalls
It's no secret that working with user profiles in Django is nothing but a misfortune. We all came across the monolithicity of the model
Everyone had to be perverted: not only to users of the dzhanga, but also to its core developers themselves. Remember, for example, how in Django 1.2 it suddenly became possible to use
We, ordinary users, also had a hard time. In order to change a user’s profile by adding any interesting fields to him - a seemingly ordinary thing, right? - had to act in different ways.
The guys who gave the world the legendary sorl.thumbnail once again excelled and made another killer thing on the account. Meet: django-primate , an application that using mankipatching techniques (primates and monkeys, can you feel the correlation?) Makes it very easy to turn your own model into
Getting started is easy enough. First you need to put django-primate. It is possible with PyPI:
... and you can also the latest version from their repository:
You need to call the patch at startup. The creators recommend using it in
now all that remains is to point to
... and you can start to come up with models:
Now, every time a component of the project addresses it
By default, the django-primate user model has the following differences from
Otherwise, the primate user’s modelwalks like a duck, swims like a duck and quacks as a duck behaves very much like the Dzhangovsky source.
Of course, you can use absolutely any number of fields, but in this case there is a risk that there will be an incompatibility between the third-party applications, so that field
Another point: for compatibility with the junga, primate will download the model so that it
does not create any migrations.
However, everyone can get acquainted with README, which I very freely translated. If I missed something, made a mistake or made an inaccuracy, write in the comments or share your thoughts about.
Thanks for attention :)
auth.User
, the inadequate set of fields in it, as well as all the tricks that had to be resorted to. Everyone had to be perverted: not only to users of the dzhanga, but also to its core developers themselves. Remember, for example, how in Django 1.2 it suddenly became possible to use
username
dog symbols (@) and dots in a field ? Do you know why? So that you can use e-mail addresses as logins . We, ordinary users, also had a hard time. In order to change a user’s profile by adding any interesting fields to him - a seemingly ordinary thing, right? - had to act in different ways.
- Take, for example, inheritance . It was necessary to create your own model, inherited from
auth.User
...# models.py from django.db import models from django.contrib.auth.models import User class MyUser(User): birthday = models.DateField()
... and write a middleware that would replace the class forrequest.user
. Or not middleware, butAUTHENTICATION_BACKEND
. It doesn’t matter :) The
advantage of the scheme was that our “client” code (that is, the project code) didn’t get complicated, we just worked with our user as with a regular dzhangovskiy.
The main disadvantage of such a scheme is that when inheriting models, not one table is filled in the database, but two: the original dzhangovskayadjango_auth
and oursourproj_user
, in which there is a foreign key ondjango_auth
. Yes, model inheritance in Django is justOneToOneField
with some additional attributes. Want to use - keep in mind. - No less well-known crutch proposed by the creators of Django - the so-called profile model . We were asked to create a
Profile
one-to-one model onauth.User
...# models.py from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user = models.OneToOneField(User) birthday = models.DateField()
... and then add something to settings:AUTH_PROFILE_MODULE = 'accounts.Profile'
After that, in the client code, we could work with the profile:profile = request.user.get_profile() profile.birthday = datetime.today() profile.save()
Plus, with such a scheme, I don’t know :) maybe any set of any fields with any name? With the minuses, everything is more transparent:- complexity of support: now we have not one object for editing, but two. We must not forget that we change the date of birth for
profile
, and, for example, the password foruser
. No wonder and get confused. - misappropriation of resources: each call to
get_profile()
causes a query to the database. On pages where there is only one user instance (editing, for example), this is not scary. If such a thing is, for example, in the comments, the result will be disastrous. Of course,select_related()
as you know, it will not save, because it does notUser
depend onProfile
, but vice versa. - and still you have to do everything with your hands! Creating a model
User
does not mean that a related model will be created automaticallyProfile
. But when referring to aget_profile()
newly created user, an exception will fly out - here there is no doubt. And although this trouble is treated in a few lines with a simple signal,# profile.models from django.db import models from django.contrib.auth.models import User class Profile(models.Model): 'что-нибудь хорошее' def create_profile(sender, **kwargs): if kwargs['created']: Profile.objects.create(user=kwargs['instance']) models.signals.post_save.connect(create_profile, sender=User)
nevertheless annoying is the need for its “manual” solution.
- complexity of support: now we have not one object for editing, but two. We must not forget that we change the date of birth for
- Mankipatching , that is, changing the behavior of a program without rewriting code. After initializing the application in some place of the project (as a rule, in the root
urls
,settings
ormodels
specially wound up application) they wrote code modifying our user:# monkey_patching.models from django.db import models from django.contrib.auth.models import User User.add_to_class('birthday', models.DateField() )
Plus, as with inheritance, ease in client code. Cons - non-obviousness. As you know, magic must be handled very carefully, because you can accidentally redefine some thing, but it will pop up in a completely different place. On the other hand, if very carefully, then why not?
Speaking of monkeys ...
The guys who gave the world the legendary sorl.thumbnail once again excelled and made another killer thing on the account. Meet: django-primate , an application that using mankipatching techniques (primates and monkeys, can you feel the correlation?) Makes it very easy to turn your own model into
auth.User
. That is, in Russian, to compose a profile of the desired fields. Getting started is easy enough. First you need to put django-primate. It is possible with PyPI:
pip install django-primate
... and you can also the latest version from their repository:
pip install -e git+https://github.com/aino/django-primate.git#egg=django-primate
You need to call the patch at startup. The creators recommend using it in
manage.py
#!/usr/bin/env python
from django.core.management import setup_environ, ManagementUtility
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write(
"Error: Can't find the file 'settings.py' in the directory "
"containing %r. It appears you've customized things.\nYou'll have to "
"run django-admin.py, passing it your settings module.\n" % __file__
)
sys.exit(1)
import settings
if __name__ == "__main__":
setup_environ(settings)
import primate
primate.patch()
ManagementUtility().execute()
now all that remains is to point to
settings
the model class that we want to useAUTH_USER_MODEL = 'users.models.User'
... and you can start to come up with models:
# users.models
from django.db import models
from primate.models import UserBase, UserMeta
class User(UserBase):
__metaclass__ = UserMeta
birthday = models.DateField()
# На что фантазии хватит?
Now, every time a component of the project addresses it
django.contrib.auth.models.User
, it will receive a model users.models.User
. The converse is also true. The admin panel will be patched automatically, no special steps are required to connect it. By default, the django-primate user model has the following differences from
auth.User
:- Fields removed
first_name
andlast_name
, but addedname
; - The maximum field length
username
is 50 characters (atauth.User
30) - Paul
email
added a unique index - The method
get_profile
returnsself
, so you can not worry about code that usesuser.get_profile()
Otherwise, the primate user’s model
Of course, you can use absolutely any number of fields, but in this case there is a risk that there will be an incompatibility between the third-party applications, so that field
username
, password
and email
it is better not to rename. Another point: for compatibility with the junga, primate will download the model so that it
app_label
becomes not users
(as in the example), but auth
. This is especially true for South users who may not understand why for some time../manage.py schemamigration users --auto
does not create any migrations.
However, everyone can get acquainted with README, which I very freely translated. If I missed something, made a mistake or made an inaccuracy, write in the comments or share your thoughts about.
Thanks for attention :)