0

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 ..

2
  • 1
    What is order, why do you need it? Why is using the default primary id field that is generated automatically not enough? Commented Aug 5, 2022 at 5:55
  • @Dunes I'm working on flashcards with leitner system , so the box could be deleted,modified, order-changed Commented Aug 5, 2022 at 12:29

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

the solution was in the last section of your answer <3, now about before it, the purpose that makes me doing this is because I'm working on the flash cards leitner system, each box could be deleted and modified, now i think you understand why I would do that rather that autoField
and here's my another question that I post it after that one, it's simpler and more readable and the answer was just as you mentioned .
I've realised that one possible problem with your defautOrder() solution is that it assumes no rows are ever deleted from the table. For instance, if you insert two boxes and then delete the first then remaining row will have an order of 2. The next value that defaultOrder() will produce is 2 (number of rows (1) plus 1)! That will violate your uniqueness constraint and break any future inserts. Instead, you should use Max() instead. eg. defaultOrder = lambda: Box.objects.aggregate(models.Max('order')) + 1
actually I wrote a whole package for Logic handling and restrictions, but because after that problem I realized that I have a lot of work on my oop knowledge , thank you very much for your Note <3

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.