python/django: pass class method to class variable

Question:

so I’m working with django-models and I want to pass the quantity of objects that class have then pass it as model argument LIKE SO:

class someclass:
    somevar = 'somevalue'
    
    def retrieve1():
        return somevar

    def retrieve2(self):
        return self.somevar

    def retrieve3():
        return someclass.somevar

    #returns NameError: name 'somevar' is not defined
    value1 = retrieve1()
    
    #returns TypeError: retrieve2() missing 1 required positional argument: 'self'
    value2 = retrieve2()
    
    #returns NameError: name 'someclass' is not defined
    value3 = retrieve3()

I even tried to pass the somevar as instance with __init__ but nothing good:

class someclass:
    def __init__(self):
        self.somevar = 'somevalue'
    
    #returns NameError: name 'self' is not defined
    value = self.somevar

what I’m missing here ????!
I tried every single thing like treat the method as @classmethod, @staticmethod nothing helped

here’s the real code i’m struggiling with:

class Box(models.Model):
    
   #so as u see, i'm trying to get the len of all objects that class holds
    def defaultOrder():
        return len(Box.objects.all()) + 1

    name = models.CharField(max_length=100, unique=True)
    order = models.IntegerField(
        blank=False, 
        unique=True,
       
       #here will be passed
        default= defaultOrder()
    )
    end_days = models.IntegerField(blank=False)
    end_hours = models.IntegerField(blank=False)
    end_minutes = models.IntegerField(blank=False)
    def __str__(self):
        return self.name

sooo as u see, I just want to treat the class as I was outside of it !
It’s realy hard to explain it in CS way, I hope you understand ..

Asked By: at_Root

||

Answers:

class Someclass:
   def __init__(self, somevar):
       self.somevar = somevar

   def retrieve2(self):
       return self.somevar


obj = Someclass('somevalue')
print(obj.retrieve2())
Answered By: Ramesh

What you are asking is simply not possible. You are trying to use the class before it has been created. That is, you are invoking defaultOrder(), before the Box class has finished being created.

It looks like you are trying to implement an auto incrementing sequence, similar to a primary key. The primary key is usually exposed as id. But you can give the field another name if you like. eg:

class Box(models.Model):
    order = models.fields.AutoField(primary_key=True)

Annoyingly, django only allows a model to have one AutoField, so you cannot create a separate order field to the default id field.
You might want to have a second AutoField if you wanted order to be changeable, so that you could reorder boxes if desired. It’s doubly annoying, because implementing something like this directly in SQL is trivial. In PostgreSQL you might do something like:

CREATE TABLE box(
    box_id SERIAL PRIMARY KEY, -- serial is an auto-incrementing 32-bit integer
    "order" SERIAL NOT NULL,
    content TEXT,
    CONSTRAINT box_order_unique UNIQUE ("order") DEFERRABLE INITIALLY IMMEDIATE
);

INSERT INTO box (content) VALUES ('a'), ('b'), ('c');
SELECT * FROM box ORDER BY "order";

gives:

box_id  order   content
1       1       a
2       2       b
3       3       c

Then you could do something like:

BEGIN;
SET CONSTRAINTS box_order_unique DEFERRED;
UPDATE box SET "order" = 2 WHERE content = 'c';
UPDATE box SET "order" = 3 WHERE content = 'b';
COMMIT;
SELECT * FROM box ORDER BY "order";

giving:

box_id  order   content
1       1       a
3       2       c
2       3       b

Passing a callable in Django

default can be a callable. Which I think is actually the behaviour you want. Each time you insert a Box you want its value to be the current count of rows in the Box table, not just a static default. This would work, because the function would only ever be invoked after the class had been created.

class Box(models.Model):
    def defaultOrder():
        # NB. count() requires less work by database than all(), and also less
        # data to be transferred from the
        return len(Box.objects.count()) + 1

    order = models.IntegerField(
        blank=False, 
        unique=True,
        # here will be passed -- NB. No brackets!
        default=defaultOrder
    )

This has the undesired side effect of causing an extra round trip to the database for each insert. Annoying again, because we know this is something the database can handle by itself.

Answered By: Dunes
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.