85

I'm trying to write an __init__ function for one of my models so that I can create an object by doing:

p = User('name','email')

When I write the model, I have:

def __init__(self, name, email, house_id, password):
    models.Model.__init__(self)
    self.name = name
    self.email = email

This works and I can save the object to the database, but when I do User.objects.all(), it doesn't pull anything up unless I take out my __init__ function. Any ideas?

3
  • 2
    Why are you doing this? That capability is part of a Django model without you writing any code at all. Commented May 9, 2009 at 16:25
  • 1
    When I try it without the init, they all go into the wrong field for some reason, unless I specify User(name='aoeu'), which takes up a lot of extra space. Commented May 9, 2009 at 16:49
  • 6
    What's wrong with explicit field=value assignments? In the long run, you'll probably be happier. After all, you write only a very few of these kinds of lines of code. Commented May 9, 2009 at 16:56

4 Answers 4

119

Relying on Django's built-in functionality and passing named parameters would be the simplest way to go.

p = User(name="Fred", email="[email protected]")

But if you're set on saving some keystrokes, I'd suggest adding a static convenience method to the class instead of messing with the initializer.

# In User class declaration
@classmethod
def create(cls, name, email):
  return cls(name=name, email=email)

# Use it
p = User.create("Fred", "[email protected]")
Sign up to request clarification or add additional context in comments.

2 Comments

Yeah, a factory method is the way to go here. You could also consider putting it on the Manager. Messing with the constructor signature will definitely break things.
@classmethod on create is also nicer
51

Django expects the signature of a model's constructor to be (self, *args, **kwargs), or some reasonable facsimile. Your changing the signature to something completely incompatible has broken it.

2 Comments

As of Django 2.1, Ignacio's answer is the correct one and should be accepted. Straight from the 2.1 docs, they say You may be tempted to customize the model by overriding the __init__ method. If you do so, however, take care not to change the calling signature as any change may prevent the model instance from being saved. from: docs.djangoproject.com/en/2.1/ref/models/instances
The link of the above comment will also have suggestions on how to do differently what you want to do. Overriding __init__ can lead to all kinds of strange bugs, as I have learned from experience this afternoon.
26

The correct answer is to avoid overriding __init__ and write a classmethod as described in the Django docs.

But this could be done like you're trying, you just need to add in *args, **kwargs to be accepted by your __init__, and pass them on to the super method call.

def __init__(self, name, email, house_id, password, *args, **kwargs):
        super(models.Model, self).__init__(self, *args, **kwargs)
        self.name = name
        self.email = email

2 Comments

super(models.Model, self).__init__(*args, **kwargs)
super(<YourModelName>, self).__init__(*args, **kwargs)
2

Don't create models with args parameters. If you make a model like so:

 User('name','email')

It becomes very unreadable very quickly as most models require more than that for initialization. You could very easily end up with:

User('Bert', 'Reynolds', '[email protected]','0123456789','5432106789',....)

Another problem here is that you don't know whether 'Bert' is the first or the last name. The last two numbers could easily be a phone number and a system id. But without it being explicit you will more easily mix them up, or mix up the order if you are using identifiers. What's more is that doing it order-based will put yet another constraint on other developers who use this method and won't remember the arbitrary order of parameters.

You should prefer something like this instead:

User(
    first_name='Bert',
    last_name='Reynolds',
    email='[email protected]',
    phone='0123456789',
    system_id='5432106789',
)

If this is for tests or something like that, you can use a factory to quickly create models. The factory boy link may be useful: http://factoryboy.readthedocs.org/en/latest/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.