Hi everyone, first time posting on the forum!
I need to know if there’s a better way to do this (Pretty sure there is
)
I have one model called Variable which stores global variables that the user may need to modify.
variables/models.py
class Variable(models.Model):
hour_price = models.DecimalField(...)
I have another model called Product with some derived attributes that need to use the above field.
products/models.py
from variables.models import Variable
v = Variable.objects.first() # Single register with some variables
hour_price = v.hour_price
class Product(models.Model):
price = models.DecimalField(...)
# Simplified for explaining
@property
def real_price(self):
price_per_hour = self.price * hour_price
return price_per_hour
The problem
When I run the project for the first time (No migrations), the following error occurs (Full traceback):
psycopg2.errors.UndefinedTable: relation "variables_variable" does not exist
LINE 1: ..., "variables_variable"."ganancia_fibrofacil" FROM "variables...
As a workaround I do the following:
- Comment the references to the
Variable
model on products/models.py
- Execute
manage.py migrate
- Undo the comments
- Now the Variable model exists so there is
What I tried
- Django Constance (Doesn’t work because I need the user to modify this global variables)
- Setting the initial migration of the Product model to depend on the initial migration of the Variable one
I expected to find a solution after writing this post but, to my disappointment, I still don;t have a better idea.
Thanks for reading.
I don’t see the problem here.
Running migrate is an expected / normal part of prepping the Django environment for execution.
I’m assuming that you want this value to be persistent across restarts? If so, then you want the data written to a table.
Ken
Thanks for answering Ken!
If so, then you want the data written to a table.
The Variable model is indeed written to a table as a normal model.
The problem for me is that everytime I want to run the system “from scratch” I need to do that workaround.
I feel there’s a better way to load the values like this
v = Variable.objects.first()
and that models.py
is not the best place to do it.
Maybe I’m overthinking the problem and, as you said, this is part of the system setup.
But it’s not a “work around”. Anytime you’re starting up a new Django instance, you need to run migrate.
(Well, the alternative would be to create an archival copy of your initialized database and restore that as part of your start-up process.)
Ok, I see the problem now. You’re trying to use Variable as a module-level variable.
Don’t. That’s just completely wrong.
Don’t try to set this up as any type of global variable. If you know that there’s only going to be one instance of “Variable”, always access it at the time and place it’s needed by retrieving it as Variable.objects.get(pk=1)
.
1 Like
I get this but it doesn’t seem right having to run migrations and then un-comment the parts of the code that give errors.
I think I understand what you say here so the question is:
Is it right to use some field from one model (Variable) into a property from another (Product)?
Right - the error is caused because you’re trying to use Variable as a module-level variable. Remove the module-level reference and the error goes away - no need at that point to comment lines.
Briefly, yes. it would be perfectly valid to write:
def real_price(self):
return self.price * Variable.objects.get(pk=1).hour_price
<opinion>
(Side note: @property
is syntactic sugar. In the context of Django, there are very few situations where it’s needed - and this isn’t one of them.)
<opinion>
1 Like
Hi Ken!
I was going to answer yesterday but I spend the rest of my day fighting with
Heroku, but that’s another story ![:sweat_smile: :sweat_smile:](https://emoji.discourse-cdn.com/twitter/sweat_smile.png?v=9)
Suffice to say your answer worked perfectly.
Sometime ago I discovered @property
on an article about managed attributes.
Then I found @classmethod
and @staticmethod
on Django models but I couldn’t find an explanation on when to choose each one.
Now I’m curious about your opinion on this topic.
Thanks Ken!
<opinion>
Context is important here. I work almost exclusively on the “endpoint” side of Django. I don’t write apps / modules / etc for other people to use. My needs are fundamentally different from those who work on more “core” or “internal” functionality like DRF, Crispy forms, Vanilla forms, Django Debug Toolbar, etc, etc, etc. The needs that those developers have are fundamentally different from mine and so their opinions are almost certainly different. This is just my perspective…
@classmethod
can be useful when you want to perform an operation on the class itself rather than on an instance.
But, given how Django works in a production-style environment - where you may have wsgi containers such as uwsgi that will run multiple instances running concurrently, and can restart instances when necessary - doing anything that alters the module or instance level can create problems - because instance X
may not be the same module instance as instance Y
. (Definitely true if you’re running multiple instances across multiple docker containers.)
@staticmethod
can be used when you want a method in a class, but has no dependencies on that class at all. I’ve seen it used to provide “utility functions” in a class without needing to maintain state within that class. You can call those methods without creating an instance of the class. However, within Django, it’s perfectly acceptable to define methods outside the context of a class.
In both cases, my uses of these features are limited to those (rare) situations where I find myself needing to do some metaprogramming - where the selection of a class needs to be determined at runtime. This allows me to create interfaces across multiple classes, where the precise implementation varies based upon the class being acted upon, without me needing to explicitly perform some kind of class test.
I put both of these into the general category of “when you run across a situation where you need to use these, you’ll know it.” Otherwise, you probably won’t need them.
<rant>
My highly opinionated position on @property
is that it’s an attempt to bring a more “Java-ish” coding style to Python, which I categorically reject. I have found a very small number (2? 3?) of situations that it has been useful to me - and again, it was clear to me in those cases that it made my code significantly “cleaner” from the perspective of someone coming behind me to work on it.
</rant>
</opinion>
Ken
Hi Ken!
I delayed my answer a little bit but I didn’t forget.
So far I used Django for the same kind of projects as you.
On @classmethod
:
I don’t have Django in a production env with multiple instances running so I’m not worried about these problems right now. (Besides the fact that I just learned what you’ve said)
On @staticmethod
:
I certainly didn’t found a use for these type of functions. I mean, if I have a function that’s not related to my model maybe I’m going to put it in some utils.py
.
On @property
As I said before, I was looking for managed attributes and I found Django docs describing it like that, so I tried them.
As always, thanks for the detailed responses you give.
Best regards, Marco.