Creating a new user object instance: model manager `create()` vs "class instantiation operator"

What is the difference between creating a user object instance using a class instantiation operator and using the model’s manager create() function?

Why would a User instance be instantiated using an operator instead of User.objects.create()?

What are the advantages to instantiating a user object instance this way?


Background

The allauth django package has a function allauth.account.adapter.new_user() that returns get_user_model()() from django.contrib.auth. (#L196)

I was not familiar with this set of double parens, but apparently this is just a compact way to place a normal “class instantiation operator.”

get_user_model() returns the user model class.

I believe the second set of parens acts similar to if you imported the User model and then did: user = User(). This apparently creates a new instance of the class.

I would normally “create” a new user object instance using some variation off User.objects.create()

The reason this is causing me some concerns is that I had overridden the User model’s create() in a custom manager. But this class instantiation operator seems to possibly sidestep my custom manager’s custom create method.

User.objects.create() creates an instance of User and commits that instance to the database.

Whereas:
a_user = User()
or
a_user = get_user_model()()
or
user_class = get_user_model()
a_user = user_class()

All create an instance of User, but does not write that instance to the database. At some point you would need to do something like a_user.save() to save the instance to the database.

See the docs for create().

Thank you, Ken.

Can you or anyone else suggest a best practice for reliably hooking into a post_create event on my custom User model?

It seems I can not count on catching it with a custom manager overriding create(), or with a post_create signal if the user is created using the way you outlined.

I could try for testing if [self.pk](http://self.pk) is None: in an overridden save() method on the model class, but that would be missed by a bulk_create.

From our perspective, the “best practice” for using signals is to not use signals. You’ve identified some of the holes in them already. I don’t believe they’re worth the trouble.
I’ve been working with Django seriously now for 8 years, and have never written a signal that has gone into a production system.

That’s not to say that they’re not useful, just that I believe the range of situations where they are useful is a lot smaller than it appears they might (or should) be.

Thanks for that additional feedback. I’m also not a fan of signals, they seem a bit like insta-spaghetti. :spaghetti:

Can you suggest techniques to use in lieu of signals to reliably execute code after a User object instance is created.

For example, monitoring if self.pk is None: in save() and carefully documenting / writing extra behavior for code that calls bulk_create.

The only sure way of executing code on a create is a database trigger.

Since bulk_create is a method on the manager, you could try overriding it in a custom manager.
e.g.

def bulk_create(self, objs, batch_size=None, ignore_conflicts=False):
    new_rows = super().bulk_create(objs, batch_size, ignore_conflicts)
    for row in new_rows:
        # Do something with each new instance
    return new_rows

(Winging this. No idea how well this would work. If this causes your computer to melt or all your data to be randomized, don’t blame me.)

:slight_smile: Thanks for this and the prior feedback, Ken.