2

I have a class Foo with two fields: an integer field x, and a comma-separated integer field n. The value of n represents the number of elements in x. When I create an instance of this class, I want to set x to a set of n zeroes, each separated by a comma.

Here is the code I have so far:

class Foo(models.Model):
    n = models.IntegerField(default=0)
    x = models.CommaSeparatedIntegerField(max_length = 100)

    def __init__(self, _n):
        super(User, self).__init__()
        self.n = _n
        self.x = [0,] * _n

This seems to work fine to construct instances of Foo. However, when I log in as admin and click on one of the Foo instances, it gives me the error: __init__() takes exactly 2 arguments (4 given)

My interpretation of this is that admin is trying to create an instance of Foo when I ask to view it, without supplying the necessary arguments (although I don't know where the 4 given comes from). So my question is: How should I be writing Python constructors with arguments, such that the instances can also be displayed in the admin page?

One alternative I can think of is to leave the default constructor, and write f = Foo(n = 5) when I create an instance. However, how can I then run the line self.x = [0,] * _n by default without having to write it as a separate function, to call whenever I create an instance?

Brian Neal
  • 31,821
  • 7
  • 55
  • 59
Karnivaurus
  • 22,823
  • 57
  • 147
  • 247
  • 1
    First, read this: https://docs.djangoproject.com/en/1.6/ref/models/instances/#creating-objects You should not override method signature. Try with `def __init__(self, *args, **kwargs)` and then get your value from `kwargs` – Germano Jul 03 '14 at 13:42

1 Answers1

2

As Germano pointed out in the comments, you should read the documentation on this subject.

You can't really override __init__ as the admin expects a certain interface. You have a couple of alternatives. Here I'll adapt the docs to your example.

Add a classmethod:

class Foo(models.Model):
    n = models.IntegerField(default=0)
    x = models.CommaSeparatedIntegerField(max_length = 100)

    @classmethod
    def create(cls, n):
        f = cls(n=n)
        f.x = # whatever
        return f

f = Foo.create(42)

Use a method on a Foo manager:

class FooManager(models.Manager):
    def create_foo(self, n):
        f= self.create(n=n)
        f.x = # something
        return f

class Foo(models.Model):
    n = models.IntegerField(default=0)
    x = models.CommaSeparatedIntegerField(max_length = 100)

    objects = FooManager()

f = Foo.objects.create_foo(42)

You really don't want to override __init__ because you can't really tell if your code is calling it, or some other part of Django (like the admin). If Django is creating a Foo object with n and x values from the database, you don't want to set your own value of x, you want to use the value from the database. I suppose you could check to see if n is supplied and not x, but it is really safer and more clear to just create your own "constructor" type methods as above.

Brian Neal
  • 31,821
  • 7
  • 55
  • 59
  • As the OP asked that http://stackoverflow.com/questions/24556655/converting-comma-separated-string-to-list I think he should mark your answer as accepted. – Germano Jul 03 '14 at 15:39
  • @Germano he might have marked it accepted right in the middle of me making an edit. I thought I saw a green check mark briefly. He might have to do it again. – Brian Neal Jul 03 '14 at 15:58